Compare commits
386 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3a500ec52 | |||
| 9a95afcdfa | |||
| 57c80cbc33 | |||
| 700620569c | |||
| 89e1c67006 | |||
| bb243fb3e5 | |||
| bf43c0a34f | |||
| 460d9530a5 | |||
| 1e9dcdee98 | |||
| 05bf2eb429 | |||
| d96c9f82fe | |||
| d195096aef | |||
| e249630e51 | |||
| 5b2f2ecd53 | |||
| 3b4fd843a4 | |||
| 227606752d | |||
| 7166bee750 | |||
| fcdc88ded7 | |||
| 142b2d639e | |||
| e1b20c59c4 | |||
| 847dca49c8 | |||
| be3e5097bc | |||
| 948bfdcce9 | |||
| c867ae1528 | |||
| 6ec7747b71 | |||
| 88143858c7 | |||
| bac755f440 | |||
| aab8c5f5ea | |||
| 4ee2101d01 | |||
| 1c2534e44c | |||
| a9d6a925e8 | |||
| 0ace1223e4 | |||
| 49f8f74125 | |||
| ed6013c295 | |||
| e79c1be43e | |||
| b5b9bfd825 | |||
| 8c2b0f9de7 | |||
| 9800eac87c | |||
| e61506c50f | |||
| b29021c352 | |||
| 474ff08431 | |||
| f691afabab | |||
| f74e3fad19 | |||
| 3ef2cac56d | |||
| aa9c978f90 | |||
| a6fd457652 | |||
| 7806f87c56 | |||
| 70b8cb0e37 | |||
| 397974cb68 | |||
| 8765c51696 | |||
| 2d3b2ed38a | |||
| 1840ceac85 | |||
| 59d0872197 | |||
| 7f918e544e | |||
| 730eb9a606 | |||
| a790f1c228 | |||
| b33f3c1a80 | |||
| b0f90da7b8 | |||
| c4ed26e3d4 | |||
| 753256cfaf | |||
| cfc7938ecc | |||
| 15de2b4272 | |||
| fed265252d | |||
| d40619d66c | |||
| 9dd913f9da | |||
| 64f46052db | |||
| eff771e640 | |||
| 397749f1f5 | |||
| 753c8af0e5 | |||
| 846e8446e0 | |||
| 385696d84a | |||
| ca9748f1ba | |||
| 7e697cfb3a | |||
| 86d2af6ca0 | |||
| 8ba8d6e15b | |||
| 3dba5827f2 | |||
| 433aa99660 | |||
| 990d9d343e | |||
| 8bc7c9d88d | |||
| 4fcd97e8da | |||
| 7a85862733 | |||
| 7308cfe259 | |||
| 58f161e019 | |||
| 693d9470f9 | |||
| 2a910e7058 | |||
| 5fad110e5e | |||
| 09531dfdc8 | |||
| e5233171ae | |||
| 8281316190 | |||
| 9594e87937 | |||
| bb9fa4ec7c | |||
| f30179a889 | |||
| 97dd77918e | |||
| 8e6a1c3aa4 | |||
| c35c9a88a9 | |||
| 43c1cd987f | |||
| 727c703bad | |||
| 0fa085c0f2 | |||
| d558ba4211 | |||
| 69de3d62a1 | |||
| c540ed8f9d | |||
| 7023dc0eb4 | |||
| 039ded92a4 | |||
| e74c9279cf | |||
| ee5a94dbe4 | |||
| b425b12cf5 | |||
| 957f5acf6a | |||
| 432abd9249 | |||
| 01a3adb662 | |||
| 81e8bc09f8 | |||
| 5758ba854a | |||
| 64b047195d | |||
| fd0976f4a2 | |||
| a80429ac27 | |||
| 8f21b64a3d | |||
| 2730f1a70c | |||
| d82dd8998d | |||
| 721d49adfb | |||
| a86aec193d | |||
| 55a0bd1643 | |||
| 2c3afb4956 | |||
| 2c8c6bf30f | |||
| 0db88e3cb4 | |||
| 7ce72b7299 | |||
| f91f928a81 | |||
| 180cf57506 | |||
| 9e888c06fa | |||
| f181694a61 | |||
| a4032c27b7 | |||
| fcc2dfb1c8 | |||
| d85eceac71 | |||
| 3c35457b60 | |||
| 10c0f0e688 | |||
| a78377da19 | |||
| 2bf4865794 | |||
| d53435420b | |||
| 3a084e1c27 | |||
| e17550b6bd | |||
| 183e18be31 | |||
| 729c5631bc | |||
| 47282ba9dd | |||
| 7c359f1622 | |||
| 37cc477d5c | |||
| dc7d923e5a | |||
| 71ac2646bb | |||
| 17db563677 | |||
| cf83106610 | |||
| 0ee8e95f62 | |||
| b3e90be70b | |||
| 20fa660797 | |||
| eebccba932 | |||
| 4b9e9e17ab | |||
| 17263316d9 | |||
| 5c8cdd8950 | |||
| 5c52a2e80e | |||
| 7ccaccee4e | |||
| afc225c8d6 | |||
| 098fd8bbc9 | |||
| 66d66a537c | |||
| bfe70c409f | |||
| cb707c3ce4 | |||
| 9c3c5228df | |||
| caebc0d6f7 | |||
| 087810a252 | |||
| 34a832b43e | |||
| d352203794 | |||
| ad54d088d5 | |||
| 2aebb119ed | |||
| a5534b8699 | |||
| a4f12da337 | |||
| e409c11e67 | |||
| 6895d9a9aa | |||
| 31d4816fcb | |||
| 8d7a3a5588 | |||
| fe5d3d11a5 | |||
| 2058101f61 | |||
| 69d4db511a | |||
| 52281ea9b5 | |||
| ab5edf2225 | |||
| 7b9f9f663d | |||
| 7bcc07f357 | |||
| 5b7c64d587 | |||
| 248990b5ef | |||
| 2acf506d16 | |||
| 43aed214f4 | |||
| f725079176 | |||
| 319b338063 | |||
| 1c30f4a3c1 | |||
| 17219dab9f | |||
| 6dfcee04ad | |||
| dbfdd417f1 | |||
| 51f6efbaad | |||
| 3d417f7742 | |||
| a258044dcc | |||
| 9a1463dd11 | |||
| 7a7790e9bd | |||
| 09da116ccd | |||
| 6a4802d556 | |||
| 352407371c | |||
| 6f40e32d49 | |||
| b84146edd5 | |||
| adfee95484 | |||
| 1f4bbece4a | |||
| 5cdec46800 | |||
| c9e091be67 | |||
| aa1a982479 | |||
| b4f2f01d73 | |||
| a04a083cd3 | |||
| 38f6aa70bd | |||
| 39f4b1c8b6 | |||
| ba2fc38a3f | |||
| c36431035f | |||
| 213d655886 | |||
| f241649bba | |||
| 4821c55242 | |||
| cf9e2c9719 | |||
| 59e0f9e7ae | |||
| e0c909536c | |||
| cbdea61a61 | |||
| c5335d4aae | |||
| 2b34b31956 | |||
| c5372c6107 | |||
| d61b30de3f | |||
| 970cbfaef6 | |||
| 7a85c43974 | |||
| 7b35a76e49 | |||
| db992de466 | |||
| e2abdaf935 | |||
| 363166ad1e | |||
| 3f306765ad | |||
| 20c0bdf60f | |||
| 46e14ea06f | |||
| 7e0f635d0e | |||
| 46e39bc109 | |||
| b04fcab423 | |||
| b23b2f6e5d | |||
| c4a7020e93 | |||
| 1411257784 | |||
| cfd556f3b3 | |||
| 257020852d | |||
| a4ff8931fe | |||
| c91bea9452 | |||
| 6386447784 | |||
| 41a656af92 | |||
| 68e5718788 | |||
| 28e3544f4a | |||
| fce55b54db | |||
| 9a068db3f1 | |||
| fadb0a1c1d | |||
| be3a797644 | |||
| f710fa79db | |||
| b724e714d3 | |||
| c6d7cf16e1 | |||
| e24f5ef8e3 | |||
| fa836008c0 | |||
| 5e8e596d40 | |||
| 7b24f868b8 | |||
| 0b4f836f2a | |||
| 8d2d4cd62d | |||
| 9666c62036 | |||
| f103b5c4b9 | |||
| 4b1df136a6 | |||
| ddf59d8c7d | |||
| c3e679d5f1 | |||
| ad11b2adb2 | |||
| e8e4687c05 | |||
| 1ecb9b49fc | |||
| f1a806b288 | |||
| 4e7f7d8413 | |||
| be5eefd44f | |||
| 2b1107c856 | |||
| 2418286a29 | |||
| 0ed65d7d3d | |||
| f76771491c | |||
| f8ec4d28bc | |||
| e666636ab7 | |||
| 900f77e7f2 | |||
| adebf727dc | |||
| 9ea9a6ecac | |||
| 3c03576518 | |||
| 0c731043ac | |||
| 2c54a9b868 | |||
| b35e132153 | |||
| 41eba65942 | |||
| 1634877666 | |||
| ff2fdeca84 | |||
| 621a4dee2c | |||
| 84856f459d | |||
| 7f0a57108d | |||
| 70a2dc7532 | |||
| 224bb047dc | |||
| 150d65c31b | |||
| af5f64b807 | |||
| 03320a2025 | |||
| 03f996c70f | |||
| c22b1cf71b | |||
| 6a49914f0f | |||
| 3a1a257f0f | |||
| 1fccae679d | |||
| 7c5236fe35 | |||
| 3b0f59cbb0 | |||
| cd9e026705 | |||
| 9ff577792b | |||
| fbfc4edf6f | |||
| 505dec6069 | |||
| 89dcc2c060 | |||
| 7ef2567da6 | |||
| 5762840f79 | |||
| 39d68618ca | |||
| b75b1c2f86 | |||
| acb7bced58 | |||
| f30987b1e5 | |||
| 713359252a | |||
| f794ab2a66 | |||
| 40addc7020 | |||
| 8e88231ede | |||
| a262db6f25 | |||
| 78e9f77950 | |||
| 03fc74e21f | |||
| ed25899b0e | |||
| 49b7a5f427 | |||
| 7f38ffac1f | |||
| 33fa0af521 | |||
| 91c303df7d | |||
| 123350ae60 | |||
| be2e09472a | |||
| 03cae0ca37 | |||
| 8e39b2ea32 | |||
| 66b24d1116 | |||
| e58815f89c | |||
| ce6e96bd20 | |||
| 80add18dc9 | |||
| 1359501d3f | |||
| a1afbb144f | |||
| 999db45b0e | |||
| f6c5fd94a7 | |||
| 681fd3d0ef | |||
| 62412ef548 | |||
| 43e506c447 | |||
| ac759a17bd | |||
| a01dc9f37a | |||
| a82a577180 | |||
| a7b16dcb97 | |||
| 093ae7de55 | |||
| 25d6a0fec4 | |||
| 7ad7827497 | |||
| a4adb124d0 | |||
| 5cfe357821 | |||
| a76c8df867 | |||
| a9a1f0e76d | |||
| ed468af4c3 | |||
| 3efca19473 | |||
| c0bc46b954 | |||
| 2c0e14cadf | |||
| d73dd18f07 | |||
| 15d37cea36 | |||
| e47108a70e | |||
| d6f2936a53 | |||
| f99fac30ea | |||
| af1dd3b9fd | |||
| 31a60eb8a3 | |||
| 318e7bb89f | |||
| 8d6a4d7071 | |||
| 7e9f15e54f | |||
| c2bae24835 | |||
| 160a793017 | |||
| 7c7b30c16e | |||
| bb94edcb15 | |||
| e2aa3c3032 | |||
| bb56cee678 | |||
| 4e9d22ada4 | |||
| cfdb2a4968 | |||
| be092f3754 | |||
| eb2c0aafd4 | |||
| 5303301d3d | |||
| 9125d089fb | |||
| aeb9f89ec1 | |||
| 7d5baf798e | |||
| a56ec333ae | |||
| dd0a541bfe | |||
| a5773c3504 | |||
| e4db84e1d7 | |||
| 542ca6cb3d | |||
| 961983e788 | |||
| f666d06485 | |||
| 1abfd39b8d |
@@ -1,7 +0,0 @@
|
|||||||
[**.c]
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
[**.yml]
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
+4
-11
@@ -1,11 +1,4 @@
|
|||||||
.idea/
|
pkg
|
||||||
prefix/
|
sysroot
|
||||||
sysroot/
|
/target/
|
||||||
**/target/
|
/test.bin
|
||||||
.gdb_history
|
|
||||||
*.patch
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
/.vim
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
|
|||||||
+20
-73
@@ -1,87 +1,34 @@
|
|||||||
image: "redoxos/redoxer:latest"
|
|
||||||
|
|
||||||
variables:
|
stages:
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
- lint
|
||||||
|
- test
|
||||||
|
|
||||||
workflow:
|
workflow:
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_COMMIT_BRANCH == "master" && $CI_PROJECT_NAMESPACE == "redox-os"'
|
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||||
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
|
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
|
||||||
|
|
||||||
stages:
|
|
||||||
- build
|
|
||||||
- cross-build
|
|
||||||
- test
|
|
||||||
before_script:
|
|
||||||
cargo install cbindgen
|
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
stage: build
|
image: "rust:latest"
|
||||||
needs: []
|
stage: lint
|
||||||
script:
|
script:
|
||||||
- rustup component add rustfmt-preview
|
- rustup component add rustfmt
|
||||||
- ./fmt.sh -- --check
|
- cargo fmt -- --check
|
||||||
|
- cd gui && cargo fmt -- --check
|
||||||
|
|
||||||
clippy:
|
cargo-test:
|
||||||
stage: build
|
image: "rust:latest"
|
||||||
needs: []
|
|
||||||
# TODO: remove when passed
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
- ./check.sh --clippy
|
|
||||||
|
|
||||||
linux:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- rustup component add clippy
|
|
||||||
- ./check.sh --host
|
|
||||||
# - ./check.sh --host --clippy
|
|
||||||
|
|
||||||
x86_64:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- ./check.sh --arch=x86_64
|
|
||||||
|
|
||||||
i586:
|
|
||||||
stage: cross-build
|
|
||||||
script:
|
|
||||||
- ./check.sh --arch=i586
|
|
||||||
# - ./check.sh --arch=i586 --clippy
|
|
||||||
|
|
||||||
aarch64:
|
|
||||||
stage: cross-build
|
|
||||||
image: "redoxos/redoxer:aarch64"
|
|
||||||
script:
|
|
||||||
- ./check.sh --arch=aarch64
|
|
||||||
# - ./check.sh --arch=aarch64 --clippy
|
|
||||||
|
|
||||||
riscv64gc:
|
|
||||||
stage: cross-build
|
|
||||||
script:
|
|
||||||
- ./check.sh --arch=riscv64gc
|
|
||||||
# - ./check.sh --arch=riscv64gc --clippy
|
|
||||||
|
|
||||||
test:linux:
|
|
||||||
stage: test
|
stage: test
|
||||||
needs: [linux]
|
|
||||||
script:
|
script:
|
||||||
- ./check.sh --host --test
|
- apt update && apt install -y fuse3 libfuse3-dev
|
||||||
|
- cargo build --locked
|
||||||
|
- cargo test
|
||||||
|
- ./target/debug/redox_installer -c res/test.toml test.bin --no-mount
|
||||||
|
|
||||||
test:x86_64:
|
gui-build:
|
||||||
stage: test
|
stage: test
|
||||||
needs: [x86_64]
|
|
||||||
script:
|
script:
|
||||||
# timeout: https://gitlab.redox-os.org/redox-os/relibc/-/issues/238
|
- apt update && apt install -y fuse3 libfuse3-dev
|
||||||
- timeout -s KILL 9m ./check.sh --arch=x86_64 --test
|
- cd gui
|
||||||
|
- redoxer build
|
||||||
test:aarch64:
|
- cargo build
|
||||||
stage: test
|
|
||||||
needs: [aarch64]
|
|
||||||
image: "redoxos/redoxer:aarch64"
|
|
||||||
# many issues that not exist in x86_64, and lack of interest to fix so far
|
|
||||||
allow_failure: true
|
|
||||||
when: manual
|
|
||||||
script:
|
|
||||||
- timeout -s KILL 9m ./check.sh --arch=aarch64 --test
|
|
||||||
|
|
||||||
#TODO: Enable more arch once dynamic linker working
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
[submodule "openlibm"]
|
|
||||||
path = openlibm
|
|
||||||
url = https://gitlab.redox-os.org/redox-os/openlibm.git
|
|
||||||
branch = master
|
|
||||||
[submodule "src/dlmalloc-rs"]
|
|
||||||
path = dlmalloc-rs
|
|
||||||
url = https://gitlab.redox-os.org/redox-os/dlmalloc-rs.git
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# format_bytes_inner was intentionally inlined as nested fn inner()
|
||||||
|
# inside format_bytes() in lib.rs — RB refactoring, not a bug.
|
||||||
|
src/installer.rs:format_bytes_inner
|
||||||
-123
@@ -1,123 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
## Table of contents
|
|
||||||
1. [What to do](#what-to-do)
|
|
||||||
2. [Code style](#code-style)
|
|
||||||
3. [Sending merge requests](#sending-merge-requests)
|
|
||||||
4. [Writing tests](#writing-tests)
|
|
||||||
5. [Running tests](#running-tests)
|
|
||||||
|
|
||||||
Maintaining a libc is tough work, and we'd love some help!
|
|
||||||
|
|
||||||
## What to do
|
|
||||||
|
|
||||||
For now, we are still trying to get full libc compatibility before we move on to
|
|
||||||
any optimisation.
|
|
||||||
|
|
||||||
- We currently have a number of unimplemented functions. Search for
|
|
||||||
`unimplemented!()` and hop right in!
|
|
||||||
- If you notice any missing functionality, feel free to add it in
|
|
||||||
|
|
||||||
## Code style
|
|
||||||
|
|
||||||
We have a `rustfmt.toml` in the root directory of relibc. Please run `./fmt.sh`
|
|
||||||
before sending in any merge requests as it will automatically format your code.
|
|
||||||
|
|
||||||
With regards to general style:
|
|
||||||
|
|
||||||
### Where applicable, prefer using references to raw pointers
|
|
||||||
|
|
||||||
This is most obvious when looking at `stdio` functions. If raw pointers were
|
|
||||||
used instead of references, then the resulting code would be significantly
|
|
||||||
uglier. Instead try to check for pointer being valid with `pointer::as_ref()`
|
|
||||||
and `pointer::as_mut()` and then immediately use those references instead.
|
|
||||||
|
|
||||||
Internal functions should always take references.
|
|
||||||
|
|
||||||
### Use the c types exposed in our platform module instead of Rust's inbuilt integer types
|
|
||||||
|
|
||||||
This is so we can guarantee that everything works across platforms. While it is
|
|
||||||
generally accepted these days that an `int` has 32 bits (which matches against
|
|
||||||
an `i32`), some platforms have `int` as having 16 bits, and others have long as
|
|
||||||
being 32 bits instead of 64. If you use the types in platform, then we can
|
|
||||||
guarantee that your code will "just work" should we port relibc to a different
|
|
||||||
architecture.
|
|
||||||
|
|
||||||
### Use our internal functions
|
|
||||||
|
|
||||||
If you need to use a C string, don't reinvent the wheel. We have functions in
|
|
||||||
the platform module that convert C strings to Rust slices.
|
|
||||||
|
|
||||||
We also have structures that wrap files, wrap writable strings, and wrap various
|
|
||||||
other commonly used things that you should use instead of rolling your own.
|
|
||||||
|
|
||||||
## Sending merge requests
|
|
||||||
|
|
||||||
If you have sent us a merge request, first of all, thanks for taking your time
|
|
||||||
to help us!
|
|
||||||
|
|
||||||
The first thing to note is that we do most of our development on our
|
|
||||||
[GitLab server](https://gitlab.redox-os.org/redox-os/relibc), and as such it is
|
|
||||||
possible that none of the maintainers will see your merge request if it is
|
|
||||||
opened on GitHub.
|
|
||||||
|
|
||||||
In your merge request, please put in the description:
|
|
||||||
- What functions (if any) have been implemented or changed
|
|
||||||
- The rationale behind your merge request (e.g. why you thought this change was
|
|
||||||
required. If you are just implementing some functions, you can ignore this)
|
|
||||||
- Any issues that are related to the merge request
|
|
||||||
|
|
||||||
We have CI attached to our GitLab instance, so all merge requests are checked to
|
|
||||||
make sure that they are tested before they are merged. Please write tests for
|
|
||||||
the functions that you add/change and test locally on your own machine
|
|
||||||
***before*** submitting a merge request.
|
|
||||||
|
|
||||||
## Writing tests
|
|
||||||
|
|
||||||
Every function that gets written needs to have a test in C in order to make sure
|
|
||||||
it works as intended. Here are a few guidelines for writing good tests.
|
|
||||||
|
|
||||||
### Ensure that any literals you have are mapped to variables instead of being directly passed to a function.
|
|
||||||
|
|
||||||
Sometimes compilers take literals put into libc functions and run them
|
|
||||||
internally during compilation, which can cause some false positives. All tests
|
|
||||||
are compiled with `-fno-builtin`, which theoretically solves this issue, but
|
|
||||||
just in case, it'd be a good idea to map inputs to variables.
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "string.h"
|
|
||||||
#include "stdio.h"
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
// Don't do this
|
|
||||||
printf("%d\n", strcspn("Hello", "Hi"));
|
|
||||||
|
|
||||||
// Do this
|
|
||||||
char *first = "Hello";
|
|
||||||
char *second = "Hi";
|
|
||||||
printf("%d\n", strcspn(first, second));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ensure your tests cover every section of code.
|
|
||||||
|
|
||||||
What happens if a string in `strcmp()` is shorter than the other string? What
|
|
||||||
happens if the first argument to `strcspn()` is longer than the second string?
|
|
||||||
In order to make sure that all functions work as expected, we ask that any tests
|
|
||||||
cover as much of the code that you have written as possible.
|
|
||||||
|
|
||||||
## Running tests
|
|
||||||
|
|
||||||
Running tests is an important part in trying to find bugs. Before opening a
|
|
||||||
merge request, we ask that you test on your own machine to make sure there are
|
|
||||||
no regressions.
|
|
||||||
|
|
||||||
You can run tests with `make test` in the root directory of relibc to compile
|
|
||||||
relibc, compile the tests and run them. This *will* print a lot of output to
|
|
||||||
stdout, so be warned!
|
|
||||||
|
|
||||||
You can test against verified correct output with `make verify` in the tests
|
|
||||||
directory. You will need to manually create the correct output and put it in the
|
|
||||||
tests/expected directory. Running any `make` commands in the tests directory
|
|
||||||
will ***not*** rebuild relibc, so you'll need to go back to the root directory
|
|
||||||
if you need to rebuild relibc.
|
|
||||||
Generated
+2194
-242
File diff suppressed because it is too large
Load Diff
+62
-140
@@ -1,154 +1,76 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "relibc"
|
name = "redox_installer"
|
||||||
version = "0.2.5+rb0.3.2"
|
version = "0.2.42+rb0.3.2"
|
||||||
|
description = "A Redox filesystem builder"
|
||||||
|
license = "MIT"
|
||||||
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
||||||
edition = "2024"
|
repository = "https://gitlab.redox-os.org/redox-os/installer"
|
||||||
|
default-run = "redox_installer"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "redox_installer"
|
||||||
|
path = "src/bin/installer.rs"
|
||||||
|
required-features = ["installer"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "redox_installer_tui"
|
||||||
|
path = "src/bin/installer_tui.rs"
|
||||||
|
required-features = ["installer"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "relibc"
|
name = "redox_installer"
|
||||||
crate-type = ["staticlib"]
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[workspace]
|
|
||||||
members = [
|
|
||||||
"src/crt0",
|
|
||||||
"src/crti",
|
|
||||||
"src/crtn",
|
|
||||||
"redox-rt",
|
|
||||||
"ld_so",
|
|
||||||
"generic-rt",
|
|
||||||
]
|
|
||||||
exclude = ["tests", "dlmalloc-rs"]
|
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
|
||||||
borrow_as_ptr = "deny"
|
|
||||||
cast_lossless = "warn" # TODO review occurrences
|
|
||||||
cast_possible_truncation = "allow" # TODO review occurrences
|
|
||||||
cast_possible_wrap = "allow" # TODO review occurrences
|
|
||||||
cast_precision_loss = "allow" # TODO review occurrences
|
|
||||||
cast_ptr_alignment = "allow" # TODO review occurrences
|
|
||||||
cast_sign_loss = "allow" # TODO review occurrences
|
|
||||||
ignored_unit_patterns = "deny"
|
|
||||||
implicit_clone = "deny"
|
|
||||||
manual_let_else = "deny"
|
|
||||||
missing_errors_doc = "allow" # TODO review occurrences
|
|
||||||
missing_panics_doc = "allow" # TODO review occurrences
|
|
||||||
missing_safety_doc = "allow" # TODO review occurrences
|
|
||||||
mut_from_ref = "deny"
|
|
||||||
precedence = "deny"
|
|
||||||
ptr_as_ptr = "warn" # TODO review occurrences
|
|
||||||
ptr_cast_constness = "warn" # TODO review occurrences
|
|
||||||
ptr_offset_by_literal = "deny"
|
|
||||||
ref_as_ptr = "warn" # TODO review occurrences
|
|
||||||
type_complexity = "allow" # TODO review occurrences
|
|
||||||
upper_case_acronyms = "allow" # TODO review occurrences
|
|
||||||
zero_ptr = "deny" # must allow on public constants due to cbindgen issue
|
|
||||||
|
|
||||||
[workspace.lints.rust]
|
|
||||||
dangling_pointers_from_temporaries = "deny"
|
|
||||||
dead_code = "allow" # TODO review occuurences
|
|
||||||
deprecated = "deny"
|
|
||||||
improper_ctypes_definitions = "deny"
|
|
||||||
internal_features = "allow" # core_intrinsics and lang_items
|
|
||||||
irrefutable_let_patterns = "deny"
|
|
||||||
mismatched_lifetime_syntaxes = "deny"
|
|
||||||
non_camel_case_types = "allow" # needed for most POSIX type names
|
|
||||||
non_snake_case = "allow" # TODO review occuurences
|
|
||||||
non_upper_case_globals = "allow" # TODO review occuurences
|
|
||||||
unexpected_cfgs = "deny"
|
|
||||||
unpredictable_function_pointer_comparisons = "deny"
|
|
||||||
unreachable_code = "allow" # TODO review occuurences
|
|
||||||
unsafe_op_in_unsafe_fn = "deny"
|
|
||||||
unused_imports = "deny"
|
|
||||||
unused_must_use = "deny"
|
|
||||||
unused_mut = "deny"
|
|
||||||
unused_unsafe = "deny"
|
|
||||||
unused_variables = "allow" # TODO review occurrences (too many for now)
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
bitflags = "2"
|
|
||||||
ioslice = { version = "0.6", default-features = false }
|
|
||||||
plain = "0.2"
|
|
||||||
redox-path = "0.4.0"
|
|
||||||
redox_protocols = { package = "libredox", path = "../libredox", default-features = false, features = ["protocol"] }
|
|
||||||
redox_syscall = { path = "../syscall" }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cc = "1"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags.workspace = true
|
anyhow = "1"
|
||||||
arrayvec = { version = "0.7.6", default-features = false }
|
arg_parser = "0.1.0"
|
||||||
cbitset = "0.2"
|
fatfs = { version = "0.3.0", optional = true }
|
||||||
posix-regex = { version = "0.1.4", features = ["no_std"] }
|
fscommon = { version = "0.1.1", optional = true }
|
||||||
|
gpt = { version = "3.0.0", optional = true }
|
||||||
rand = { version = "0.10", default-features = false }
|
libc = { version = "0.2.70", optional = true }
|
||||||
rand_xorshift = "0.5"
|
pkgar = { version = "0.2.2", optional = true }
|
||||||
rand_jitter = "0.6"
|
pkgar-core = { version = "0.2.2", optional = true }
|
||||||
|
pkgar-keys = { version = "0.2.2", optional = true }
|
||||||
memchr = { version = "2.2.0", default-features = false }
|
rand = { version = "0.9", optional = true }
|
||||||
plain.workspace = true
|
redox-pkg = { version = "0.3.1", features = ["indicatif"], optional = true }
|
||||||
unicode-width = "0.1"
|
redox_syscall = { path = "../syscall", optional = true }
|
||||||
__libc_only_for_layout_checks = { package = "libc", version = "0.2.149", optional = true }
|
redoxfs = { path = "../redoxfs", optional = true, default-features = false, features = ["std", "log"] }
|
||||||
md5-crypto = { package = "md-5", version = "0.10.6", default-features = false }
|
rust-argon2 = { version = "3", optional = true }
|
||||||
sha-crypt = { version = "0.5", default-features = false }
|
serde = "1"
|
||||||
base64ct = { version = "1.6", default-features = false, features = ["alloc"] }
|
serde_derive = "1.0"
|
||||||
bcrypt-pbkdf = { version = "0.10", default-features = false, features = [
|
termion = { version = "4", optional = true }
|
||||||
"alloc",
|
toml = "0.8"
|
||||||
] }
|
uuid = { version = "1.4", features = ["v4"], optional = true }
|
||||||
scrypt = { version = "0.11", default-features = false, features = ["simple"] }
|
|
||||||
pbkdf2 = { version = "0.12", features = ["sha2"] }
|
|
||||||
sha2 = { version = "0.10", default-features = false }
|
|
||||||
generic-rt = { path = "generic-rt" }
|
|
||||||
chrono-tz = { version = "0.10", default-features = false }
|
|
||||||
chrono = { version = "0.4", default-features = false, features = ["alloc"] }
|
|
||||||
libm = "0.2"
|
|
||||||
log = "0.4"
|
|
||||||
spin = "0.9.8"
|
|
||||||
argon2 = "0.5.3"
|
|
||||||
|
|
||||||
[dependencies.dlmalloc]
|
|
||||||
path = "dlmalloc-rs"
|
|
||||||
default-features = false
|
|
||||||
features = ["c_api"]
|
|
||||||
|
|
||||||
[dependencies.object]
|
|
||||||
version = "0.36.7"
|
|
||||||
git = "https://gitlab.redox-os.org/andypython/object"
|
|
||||||
branch = "new"
|
|
||||||
default-features = false
|
|
||||||
features = ["elf", "read_core"]
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
sc = "0.2.7"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "redox")'.dependencies]
|
[target.'cfg(target_os = "redox")'.dependencies]
|
||||||
redox_syscall.workspace = true
|
libredox = { path = "../libredox", optional = true }
|
||||||
redox-rt = { path = "redox-rt" }
|
ring = { version = "=0.17.8", optional = true }
|
||||||
redox-path.workspace = true
|
|
||||||
redox_event = { version = "0.4.8", default-features = false, features = ["redox_syscall"] }
|
|
||||||
ioslice.workspace = true
|
|
||||||
redox-ioctl = { path = "redox-ioctl" }
|
|
||||||
redox_protocols.workspace = true
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# to enable trace level, take out this `no_trace`
|
default = ["installer", "fuse"]
|
||||||
default = ["check_against_libc_crate", "no_trace"]
|
installer = [
|
||||||
check_against_libc_crate = ["__libc_only_for_layout_checks"]
|
"fatfs",
|
||||||
math_libm = []
|
"fscommon",
|
||||||
no_trace = ["log/release_max_level_debug"]
|
"gpt",
|
||||||
# for very verbose activity beyond trace level
|
"libc",
|
||||||
trace_tls = []
|
"libredox",
|
||||||
|
"pkgar",
|
||||||
[profile.dev]
|
"pkgar-core",
|
||||||
panic = "abort"
|
"pkgar-keys",
|
||||||
|
"rand",
|
||||||
[profile.release]
|
"redox-pkg",
|
||||||
panic = "abort"
|
"redox_syscall",
|
||||||
|
"redoxfs",
|
||||||
|
"ring",
|
||||||
|
"rust-argon2",
|
||||||
|
"termion",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
fuse = ["redoxfs/fuse"]
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
cc-11 = { git = "https://github.com/tea/cc-rs", branch = "riscv-abi-arch-fix", package = "cc" }
|
# https://github.com/briansmith/ring/issues/1999
|
||||||
|
ring = { git = "https://gitlab.redox-os.org/redox-os/ring.git", branch = "redox-0.17.8" }
|
||||||
redox_syscall = { path = "../syscall" }
|
redox_syscall = { path = "../syscall" }
|
||||||
libredox = { path = "../libredox" }
|
libredox = { path = "../libredox" }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018 Redox OS
|
Copyright (c) 2017 Redox OS
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,238 +0,0 @@
|
|||||||
include config.mk
|
|
||||||
|
|
||||||
CARGO?=cargo
|
|
||||||
CARGO_TEST?=$(CARGO)
|
|
||||||
CARGO_COMMON_FLAGS=-Z build-std=core,alloc,compiler_builtins
|
|
||||||
CARGOFLAGS?=$(CARGO_COMMON_FLAGS)
|
|
||||||
CC_WRAPPER?=
|
|
||||||
RUSTCFLAGS?=
|
|
||||||
LINKFLAGS?=-lgcc
|
|
||||||
USE_RUST_LIBM?=
|
|
||||||
TESTBIN?=
|
|
||||||
export OBJCOPY?=objcopy
|
|
||||||
|
|
||||||
export CARGO_TARGET_DIR?=$(shell pwd)/target
|
|
||||||
BUILD?=$(CARGO_TARGET_DIR)/$(TARGET)
|
|
||||||
CARGOFLAGS+=--target=$(TARGET)
|
|
||||||
EXCEPT_MATH=-not -name "math"
|
|
||||||
FEATURE_MATH=
|
|
||||||
ifneq ($(USE_RUST_LIBM),)
|
|
||||||
FEATURE_MATH=--features math_libm
|
|
||||||
EXCEPT_MATH=
|
|
||||||
endif
|
|
||||||
|
|
||||||
TARGET_HEADERS?=$(BUILD)/include
|
|
||||||
export CFLAGS=-I$(TARGET_HEADERS)
|
|
||||||
|
|
||||||
PROFILE?=release
|
|
||||||
|
|
||||||
HEADERS_UNPARSED=$(shell find src/header -mindepth 1 -maxdepth 1 -type d -not -name "_*" $(EXCEPT_MATH) -printf "%f\n")
|
|
||||||
HEADERS_DEPS=$(shell find src/header -type f \( -name "cbindgen.toml" -o -name "*.rs" \))
|
|
||||||
#HEADERS=$(patsubst %,%.h,$(subst _,/,$(HEADERS_UNPARSED)))
|
|
||||||
|
|
||||||
SRC=\
|
|
||||||
Cargo.* \
|
|
||||||
$(shell find src/ redox-rt/src/ ld_so/src/ redox-ioctl/src/ include/ -type f)
|
|
||||||
|
|
||||||
BUILTINS_VERSION=0.1.70
|
|
||||||
|
|
||||||
.PHONY: all clean fmt install install-libs install-headers install-tests libs headers submodules test
|
|
||||||
|
|
||||||
all: | headers libs
|
|
||||||
|
|
||||||
headers: $(BUILD)/$(PROFILE)/librelibc.a $(HEADERS_DEPS)
|
|
||||||
rm -rf $(TARGET_HEADERS)
|
|
||||||
mkdir -p $(TARGET_HEADERS)
|
|
||||||
cp -r include/* $(TARGET_HEADERS)
|
|
||||||
ifeq ($(USE_RUST_LIBM),)
|
|
||||||
cp "openlibm/include"/*.h $(TARGET_HEADERS)
|
|
||||||
cp "openlibm/src"/*.h $(TARGET_HEADERS)
|
|
||||||
endif
|
|
||||||
@set -e ; \
|
|
||||||
for header in $(HEADERS_UNPARSED); do \
|
|
||||||
if test -f "src/header/$$header/cbindgen.toml"; then \
|
|
||||||
printf "\033[0;36;49mWriting Header $$header\033[0m\n"; \
|
|
||||||
out=`echo "$$header" | sed 's/_/\//g'`; \
|
|
||||||
out="$(TARGET_HEADERS)/$$out.h"; \
|
|
||||||
cat "src/header/$$header/cbindgen.toml" cbindgen.globdefs.toml \
|
|
||||||
| cbindgen "src/header/$$header/mod.rs" --config=/dev/stdin --output "$$out"; \
|
|
||||||
fi \
|
|
||||||
done; printf "\033[0;36;49mAll headers written\033[0m\n";
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(CARGO) clean
|
|
||||||
$(MAKE) -C tests clean
|
|
||||||
rm -rf sysroot
|
|
||||||
|
|
||||||
check:
|
|
||||||
$(CARGO) check
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
./fmt.sh
|
|
||||||
|
|
||||||
install-headers: headers libs
|
|
||||||
mkdir -pv "$(DESTDIR)/include"
|
|
||||||
cp -rv "$(TARGET_HEADERS)"/* "$(DESTDIR)/include"
|
|
||||||
|
|
||||||
libs: \
|
|
||||||
$(BUILD)/$(PROFILE)/libc.a \
|
|
||||||
$(BUILD)/$(PROFILE)/libc.so \
|
|
||||||
$(BUILD)/$(PROFILE)/crt0.o \
|
|
||||||
$(BUILD)/$(PROFILE)/crti.o \
|
|
||||||
$(BUILD)/$(PROFILE)/crtn.o
|
|
||||||
|
|
||||||
install-libs: headers libs
|
|
||||||
mkdir -pv "$(DESTDIR)/lib"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/libc.a" "$(DESTDIR)/lib"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/libc.so" "$(DESTDIR)/lib"
|
|
||||||
ln -vnfs libc.so "$(DESTDIR)/lib/libc.so.6"
|
|
||||||
ln -vnfs libc.so "$(DESTDIR)/lib/$(LD_SONAME)"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/crt0.o" "$(DESTDIR)/lib"
|
|
||||||
ln -vnfs crt0.o "$(DESTDIR)/lib/crt1.o"
|
|
||||||
ln -vnfs crt0.o "$(DESTDIR)/lib/Scrt1.o"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/crti.o" "$(DESTDIR)/lib"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/crtn.o" "$(DESTDIR)/lib"
|
|
||||||
ifeq ($(USE_RUST_LIBM),)
|
|
||||||
cp -v "$(BUILD)/openlibm/libopenlibm.a" "$(DESTDIR)/lib/libm.a"
|
|
||||||
else
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/libm.a"
|
|
||||||
endif
|
|
||||||
# Empty compatibility archives. relibc implements these APIs inside libc
|
|
||||||
# itself, but build systems still pass -ldl/-lpthread/-lrt/-lcrypt/-lutil
|
|
||||||
# because that is where glibc puts them. Shipping empty archives lets the
|
|
||||||
# link resolve and the symbols come from libc -- the same approach musl
|
|
||||||
# takes. Without libcrypt.a, KDE's kcm_users fails with
|
|
||||||
# ld: cannot find -lcrypt: No such file or directory
|
|
||||||
# even though libc.a exports both crypt and crypt_r.
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/libdl.a"
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/libpthread.a"
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/librt.a"
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/libcrypt.a"
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/libutil.a"
|
|
||||||
|
|
||||||
install-tests: tests
|
|
||||||
$(MAKE) -C tests
|
|
||||||
mkdir -p "$(DESTDIR)/relibc-tests"
|
|
||||||
cp -vr tests/build_$(TARGET)/* "$(DESTDIR)/relibc-tests/"
|
|
||||||
|
|
||||||
install: install-headers install-libs
|
|
||||||
|
|
||||||
submodules:
|
|
||||||
git submodule sync
|
|
||||||
git submodule update --init --recursive
|
|
||||||
|
|
||||||
sysroot:
|
|
||||||
@mkdir -p $@
|
|
||||||
|
|
||||||
.PHONY: sysroot/$(TARGET)
|
|
||||||
sysroot/$(TARGET): | sysroot
|
|
||||||
rm -rf $@
|
|
||||||
rm -rf $@.partial
|
|
||||||
mkdir -p $@.partial
|
|
||||||
$(MAKE) install DESTDIR=$(shell pwd)/$@.partial
|
|
||||||
mv $@.partial $@
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
test: sysroot/$(TARGET)
|
|
||||||
# TODO: Fix SIGILL when running cargo test
|
|
||||||
# $(CARGO_TEST) test
|
|
||||||
$(MAKE) -C tests run
|
|
||||||
|
|
||||||
test-once: sysroot/$(TARGET)
|
|
||||||
$(MAKE) -C tests run-once TESTBIN=$(TESTBIN)
|
|
||||||
|
|
||||||
|
|
||||||
$(BUILD)/$(PROFILE)/libc.so: $(BUILD)/$(PROFILE)/ld_so.o $(BUILD)/$(PROFILE)/libc.a
|
|
||||||
# Specifying a dynamic list makes all non-local `STV_DEFAULT`
|
|
||||||
# definitions non-preemptible, except for the symbols listed in
|
|
||||||
# `dynlst.txt`. So, the symbols defined in `dynlst.txt` may be
|
|
||||||
# interposed by other definitions at runtime (either from the
|
|
||||||
# executable or some shared library).
|
|
||||||
$(CC) -nostdlib \
|
|
||||||
-shared \
|
|
||||||
-Wl,--gc-sections \
|
|
||||||
-Wl,--dynamic-list=dynlst.txt \
|
|
||||||
-Wl,-z,pack-relative-relocs \
|
|
||||||
-Wl,--sort-common \
|
|
||||||
-Wl,--whole-archive $^ -Wl,--no-whole-archive \
|
|
||||||
-Wl,-soname,libc.so.6 \
|
|
||||||
$(LINKFLAGS) \
|
|
||||||
-o $@
|
|
||||||
|
|
||||||
$(BUILD)/$(PROFILE)/libc.a: $(BUILD)/$(PROFILE)/librelibc.a $(BUILD)/openlibm/libopenlibm.a
|
|
||||||
echo "create $@" > "$@.mri"
|
|
||||||
for lib in $^; do\
|
|
||||||
echo "addlib $$lib" >> "$@.mri"; \
|
|
||||||
done
|
|
||||||
echo "save" >> "$@.mri"
|
|
||||||
echo "end" >> "$@.mri"
|
|
||||||
$(AR) -M < "$@.mri"
|
|
||||||
|
|
||||||
# Debug targets
|
|
||||||
|
|
||||||
$(BUILD)/debug/librelibc.a: $(SRC)
|
|
||||||
$(CARGO) rustc $(CARGOFLAGS) $(FEATURE_MATH) -- --emit link=$@ -g -C debug-assertions=no $(RUSTCFLAGS)
|
|
||||||
./renamesyms.sh "$@" "$(BUILD)/debug/deps/"
|
|
||||||
./stripcore.sh "$@"
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/debug/crt0.o: $(SRC)
|
|
||||||
$(CARGO) rustc --manifest-path src/crt0/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/debug/crti.o: $(SRC)
|
|
||||||
$(CARGO) rustc --manifest-path src/crti/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/debug/crtn.o: $(SRC)
|
|
||||||
$(CARGO) rustc --manifest-path src/crtn/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/debug/ld_so.o: $(SRC)
|
|
||||||
$(CARGO) rustc --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort -g -C debug-assertions=no $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
# Release targets
|
|
||||||
|
|
||||||
$(BUILD)/release/librelibc.a: $(SRC)
|
|
||||||
$(CARGO) rustc --release $(CARGOFLAGS) $(FEATURE_MATH) -- --emit link=$@ $(RUSTCFLAGS)
|
|
||||||
@# TODO: Better to only allow a certain whitelisted set of symbols? Perhaps
|
|
||||||
@# use some cbindgen hook, specify them manually, or grep for #[unsafe(no_mangle)].
|
|
||||||
./renamesyms.sh "$@" "$(BUILD)/release/deps/"
|
|
||||||
./stripcore.sh "$@"
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/release/crt0.o: $(SRC)
|
|
||||||
$(CARGO) rustc --release --manifest-path src/crt0/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/release/crti.o: $(SRC)
|
|
||||||
$(CARGO) rustc --release --manifest-path src/crti/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/release/crtn.o: $(SRC)
|
|
||||||
$(CARGO) rustc --release --manifest-path src/crtn/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/release/ld_so.o: $(SRC)
|
|
||||||
$(CARGO) rustc --release --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
# Other targets
|
|
||||||
|
|
||||||
$(BUILD)/openlibm: openlibm
|
|
||||||
rm -rf $@ $@.partial
|
|
||||||
mkdir -p $(BUILD)
|
|
||||||
cp -r $< $@.partial
|
|
||||||
mv $@.partial $@
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
ifeq ($(USE_RUST_LIBM),)
|
|
||||||
$(BUILD)/openlibm/libopenlibm.a: $(BUILD)/openlibm headers $(BUILD)/$(PROFILE)/librelibc.a
|
|
||||||
$(MAKE) -s AR=$(AR) CC="$(CC_WRAPPER) $(CC)" LD=$(LD) CPPFLAGS="$(CPPFLAGS) -fno-stack-protector -I$(shell pwd)/include -I$(TARGET_HEADERS)" -C $< libopenlibm.a
|
|
||||||
./renamesyms.sh "$@" "$(BUILD)/$(PROFILE)/deps/"
|
|
||||||
else
|
|
||||||
$(BUILD)/openlibm/libopenlibm.a:
|
|
||||||
mkdir -p "$(BUILD)/openlibm"
|
|
||||||
$(AR) -rcs "$(BUILD)/openlibm/libopenlibm.a"
|
|
||||||
endif
|
|
||||||
@@ -1,173 +1,50 @@
|
|||||||
# Redox C Library (relibc)
|
# Redox OS installer
|
||||||
|
|
||||||
relibc is a portable C standard library written in Rust and is under heavy development, this library contains the following items:
|
The Redox installer will allow you to produce a Redox OS image. You will
|
||||||
|
be able to specify:
|
||||||
|
- Output device (raw image, ISO, QEMU, VirtualBox, drive)
|
||||||
|
- Filesystem
|
||||||
|
- Included packages
|
||||||
|
- Method of installation (from source, from binary)
|
||||||
|
- User accounts
|
||||||
|
|
||||||
- C, Linux, BSD functions and extensions
|
You will be prompted to install dependencies, based on your OS and method of
|
||||||
- POSIX compatibility layer
|
installation. The easiest method is to install from binaries.
|
||||||
- Interfaces for system components
|
|
||||||
|
|
||||||
The motivation for this project is twofold: Reduce issues that the Redox developers were having with [newlib](https://sourceware.org/newlib/), and create a more stable and safe alternative to C standard libraries written in C. It is mainly designed to be used under Redox, as an alternative to newlib, but it also supports Linux via the [sc](https://crates.io/crates/sc) crate.
|
## Usage
|
||||||
|
|
||||||
Currently Redox and Linux are supported.
|
It is recommended to compile with `cargo`, in release mode:
|
||||||
|
```bash
|
||||||
## `redox-rt`
|
cargo build --release
|
||||||
|
|
||||||
`redox-rt` is a runtime library that provides much of the code that enables POSIX on Redox, like `fork`, `exec`, signal handling, etc.
|
|
||||||
Relibc uses it as backend in `src/platform/redox`, and it's intended to eventually be usable independently, without relibc.
|
|
||||||
|
|
||||||
## Repository Layout
|
|
||||||
|
|
||||||
- `include` - Header files (mostly macros and variadic functions `cbindgen` can't generate)
|
|
||||||
- `src` - Source files
|
|
||||||
- `src/c` - C code
|
|
||||||
- `src/crt0` - Runtime code
|
|
||||||
- `src/crti` - Runtime code
|
|
||||||
- `src/crtn` - Runtime code
|
|
||||||
- `src/header` - Header files implementation
|
|
||||||
- `src/header/*` - Each folder has a `cbindgen.toml` file, it generates a C-to-Rust interface and header files
|
|
||||||
- `src/ld_so` - Dynamic loader code
|
|
||||||
- `src/platform` - Platform-specific and common code
|
|
||||||
- `src/platform/redox` - Redox-specific code
|
|
||||||
- `src/platform/linux` - Linux-specific code
|
|
||||||
- `src/pthread` - pthread implementation
|
|
||||||
- `src/sync` - Synchronization primitives
|
|
||||||
- `tests` - C tests (each MR needs to give success in all of them)
|
|
||||||
|
|
||||||
## Download the sources
|
|
||||||
|
|
||||||
To download the relibc sources run the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone --recursive https://gitlab.redox-os.org/redox-os/relibc
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build Instructions
|
By default, you will be prompted to supply configuration options. You can
|
||||||
|
use the scripted mode by supplying a configuration file:
|
||||||
|
```bash
|
||||||
|
cargo run --release -- config/example.toml
|
||||||
|
```
|
||||||
|
An example configuration can be found in [config/example.toml](./config/example.toml).
|
||||||
|
Unsuplied configuration will use the default. You can use the `general.prompt`
|
||||||
|
setting to prompt when configuration is not set. Multiple configurations can
|
||||||
|
be specified, they will be built in order.
|
||||||
|
|
||||||
To build relibc out of the Redox build system, do the following steps:
|
## Embedding
|
||||||
|
|
||||||
### Dependencies
|
The installer can also be used inside of other crates, as a library:
|
||||||
|
|
||||||
- Install `cbindgen`
|
```toml
|
||||||
|
# Cargo.toml
|
||||||
```sh
|
[dependencies]
|
||||||
cargo install cbindgen
|
redox_installer = "0.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Install the `expect` tool
|
```rust
|
||||||
|
// src/main.rs
|
||||||
|
extern crate redox_installer;
|
||||||
|
|
||||||
- Debian, Ubuntu and PopOS:
|
fn main() {
|
||||||
|
let mut config = redox_installer::Config::default();
|
||||||
```sh
|
...
|
||||||
sudo apt install expect
|
redox_installer::install(config);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- Fedora:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo dnf install expect
|
|
||||||
```
|
|
||||||
|
|
||||||
- Arch Linux:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo pacman -S expect
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build Relibc
|
|
||||||
|
|
||||||
To build the relibc library objects, run the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make all
|
|
||||||
```
|
|
||||||
|
|
||||||
- Clean old library objects and tests
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make clean
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build relibc inside the Redox build system
|
|
||||||
|
|
||||||
Inside of your Redox build system, run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make prefix
|
|
||||||
```
|
|
||||||
|
|
||||||
If you need to rebuild `relibc` for testing a Cookbook recipe, run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
touch relibc
|
|
||||||
make prefix r.recipe-name
|
|
||||||
```
|
|
||||||
|
|
||||||
Touching (changing the "last modified time" of) the `relibc` folder is needed to trigger recompilation for `make prefix`. Replace `recipe-name` with your desired recipe name.
|
|
||||||
|
|
||||||
Note: Do not edit `relibc` inside `prefix` folder! Do your work on `relibc` folder directly inside your Redox build system instead.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Relibc has a test suite that also runs every time a new commit get pushed. You can see `.gitlab-ci.yml` to see how it's being executed. That being said, `./check.sh` is the recommended way to run tests. Here's few examples:
|
|
||||||
|
|
||||||
+ `./check.sh` - Run build, without running the test
|
|
||||||
+ `./check.sh --test` - Run all tests in x86_64 Redox using Redoxer
|
|
||||||
+ `./check.sh --test --host` - Run all tests in host (Linux)
|
|
||||||
+ `./check.sh --test --arch=aarch64` - Run all tests in specified arch
|
|
||||||
- Arch can be `x86_64`, `aarch64`, `i586`, or `riscv64gc`
|
|
||||||
+ `./check.sh --test=stdio/printf` - Run a single test
|
|
||||||
- Can be combined with `--host` or `--arch`
|
|
||||||
- Will run statically linked test in Linux, dynamically linked in Redox
|
|
||||||
|
|
||||||
Couple of notes:
|
|
||||||
|
|
||||||
- Relibc and its tests will rebuild if files are changed, however switching between arch or host requires you to run `make clean`
|
|
||||||
- Redoxer is needed to run tests for Redox without `--host`. You can install it using `cargo install redoxer`
|
|
||||||
- Tests can hang, the test runner can anticipate this, assuming the kernel doesn't hang too.
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
#### I'm building for my own platform which I run, and am getting `x86_64-linux-gnu-ar: command not found` (or similar)
|
|
||||||
|
|
||||||
The Makefile expects GNU compiler tools prefixed with the platform specifier, as would be present when you installed a cross compiler. Since you are building for your own platform, some Linux distributions (like Manjaro) don't install/symlink the prefixed executables.
|
|
||||||
|
|
||||||
An easy fix would be to replace the corresponding lines in `config.mk`, e.g.
|
|
||||||
|
|
||||||
```diff
|
|
||||||
ifeq ($(TARGET),x86_64-unknown-linux-gnu)
|
|
||||||
- export CC=x86_64-linux-gnu-gcc
|
|
||||||
- export LD=x86_64-linux-gnu-ld
|
|
||||||
- export AR=x86_64-linux-gnu-ar
|
|
||||||
- export NM=x86_64-linux-gnu-nm
|
|
||||||
+ export CC=gcc
|
|
||||||
+ export LD=ld
|
|
||||||
+ export AR=ar
|
|
||||||
+ export NM=nm
|
|
||||||
export OBJCOPY=objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SO_PATH=lib/ld64.so.1
|
|
||||||
endif
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Before starting to contribute, read [this](CONTRIBUTING.md) document.
|
|
||||||
|
|
||||||
## Supported OSes
|
|
||||||
|
|
||||||
- Redox OS
|
|
||||||
- Linux
|
|
||||||
|
|
||||||
## Supported architectures
|
|
||||||
|
|
||||||
- i586 (Intel/AMD)
|
|
||||||
- x86_64 (Intel/AMD)
|
|
||||||
- aarch64 (ARM64)
|
|
||||||
- riscv64gc (RISC-V)
|
|
||||||
|
|
||||||
## Funding - _Unix-style Signals and Process Management_
|
|
||||||
|
|
||||||
This project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals).
|
|
||||||
|
|
||||||
[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" />](https://nlnet.nl)
|
|
||||||
[<img src="https://nlnet.nl/image/logos/NGI0_tag.svg" alt="NGI Zero Logo" width="20%" />](https://nlnet.nl/core)
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
extern crate cc;
|
|
||||||
|
|
||||||
use std::{env, fs};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let _crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
|
|
||||||
let target = env::var("TARGET").unwrap();
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=src/c");
|
|
||||||
|
|
||||||
let mut cc_builder = &mut cc::Build::new();
|
|
||||||
|
|
||||||
cc_builder = cc_builder.flag("-nostdinc").flag("-nostdlib");
|
|
||||||
|
|
||||||
if target.starts_with("aarch64") {
|
|
||||||
cc_builder = cc_builder.flag("-mno-outline-atomics")
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_builder
|
|
||||||
.flag("-fno-stack-protector")
|
|
||||||
.flag("-Wno-expansion-to-defined")
|
|
||||||
.files(
|
|
||||||
fs::read_dir("src/c")
|
|
||||||
.expect("src/c directory missing")
|
|
||||||
.map(|res| res.expect("read_dir error").path()),
|
|
||||||
)
|
|
||||||
.compile("relibc_c");
|
|
||||||
|
|
||||||
println!("cargo:rustc-link-lib=static=relibc_c");
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
# needs a leading newline
|
|
||||||
[defines]
|
|
||||||
"target_os=redox" = "__redox__"
|
|
||||||
"target_os=linux" = "__linux__"
|
|
||||||
"target_pointer_width=64" = "__LP64__"
|
|
||||||
"target_pointer_width=32" = "__ILP32__"
|
|
||||||
"target_arch=x86" = "__i386__"
|
|
||||||
"target_arch=x86_64" = "__x86_64__"
|
|
||||||
"target_arch=aarch64" = "__aarch64__"
|
|
||||||
# This is not exact. It should be `defined(__riscv) && defined(__LP64__)`, or `defined(__riscv) && __riscv_xlen==64`
|
|
||||||
# This will do however, as long as we only support riscv64 and not riscv32
|
|
||||||
"target_arch=riscv64" = "__riscv"
|
|
||||||
|
|
||||||
# XXX: silences a warning
|
|
||||||
"feature = no_std" = "__relibc__"
|
|
||||||
"feature = ip6" = "__ip6__"
|
|
||||||
|
|
||||||
# Ensure attributes are passed down from Rust
|
|
||||||
# <features.h> must be included where attributes are used in relibc
|
|
||||||
[fn]
|
|
||||||
must_use = "__nodiscard"
|
|
||||||
deprecated = "__deprecated"
|
|
||||||
deprecated_with_note = "__deprecatedNote({})"
|
|
||||||
no_return = "__noreturn"
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
RED='\033[1;38;5;196m'
|
|
||||||
GREEN='\033[1;38;5;46m'
|
|
||||||
NC='\033[0m'
|
|
||||||
|
|
||||||
show_help() {
|
|
||||||
echo "Usage: $(basename "$0") [OPTIONS]"
|
|
||||||
echo ""
|
|
||||||
echo "Description:"
|
|
||||||
echo " Wrapper for Makefile / Cargo to run checks or tests on Redox OS targets."
|
|
||||||
echo ""
|
|
||||||
echo "Options:"
|
|
||||||
echo " --test Run 'make test' instead of 'make all'"
|
|
||||||
echo " --test= Run single 'make test'"
|
|
||||||
echo " --cargo Run 'cargo check' / 'cargo test' instead"
|
|
||||||
echo " (note: cargo test is currently not maintained for relibc)"
|
|
||||||
echo " --host Run the command on host (linux) target"
|
|
||||||
echo " --all-target Run the command on all supported Redox architectures"
|
|
||||||
echo " --target=<target> Override the target architecture (e.g., i586-unknown-redox)"
|
|
||||||
echo " --arch=<arch> Override the target architecture using arch (e.g., i586)"
|
|
||||||
echo " --help Show this help message"
|
|
||||||
echo ""
|
|
||||||
echo "Supported Targets:"
|
|
||||||
for t in "${SUPPORTED_TARGETS[@]}"; do
|
|
||||||
echo " - $t"
|
|
||||||
done
|
|
||||||
echo " - $(uname -m)-unknown-linux-gnu"
|
|
||||||
echo ""
|
|
||||||
echo "Environment:"
|
|
||||||
echo " TARGET Sets the default target (overridden by --target)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ! command -v cbindgen &> /dev/null; then
|
|
||||||
echo "Error: 'cbindgen' CLI not found."
|
|
||||||
echo "Please install it: cargo install cbindgen"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
SUPPORTED_TARGETS=(
|
|
||||||
"x86_64-unknown-redox"
|
|
||||||
"i586-unknown-redox"
|
|
||||||
"aarch64-unknown-redox"
|
|
||||||
"riscv64gc-unknown-redox"
|
|
||||||
)
|
|
||||||
|
|
||||||
CURRENT_TARGET="${TARGET:-x86_64-unknown-redox}"
|
|
||||||
CHECK_ALL=false
|
|
||||||
CMD_ACTION="make"
|
|
||||||
CARGO_ACTION="check"
|
|
||||||
MAKE_ACTION="all"
|
|
||||||
TEST_BIN=""
|
|
||||||
IS_HOST=0
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
--all-target)
|
|
||||||
CHECK_ALL=true
|
|
||||||
;;
|
|
||||||
--test)
|
|
||||||
MAKE_ACTION="test"
|
|
||||||
CARGO_ACTION="test"
|
|
||||||
;;
|
|
||||||
--test=*)
|
|
||||||
TEST_BIN="${1#*=}"
|
|
||||||
MAKE_ACTION="test-once"
|
|
||||||
;;
|
|
||||||
--cargo)
|
|
||||||
CMD_ACTION="cargo"
|
|
||||||
;;
|
|
||||||
--clippy)
|
|
||||||
CMD_ACTION="cargo"
|
|
||||||
CARGO_ACTION="clippy"
|
|
||||||
;;
|
|
||||||
--host)
|
|
||||||
CURRENT_TARGET="$(uname -m)-unknown-linux-gnu"
|
|
||||||
IS_HOST=1
|
|
||||||
;;
|
|
||||||
--target=*)
|
|
||||||
CURRENT_TARGET="${1#*=}"
|
|
||||||
;;
|
|
||||||
--arch=*)
|
|
||||||
CURRENT_TARGET="${1#*=}-unknown-redox"
|
|
||||||
;;
|
|
||||||
--help)
|
|
||||||
show_help
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo -e "${RED}Error: Unknown option '$1'${NC}"
|
|
||||||
show_help
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$IS_HOST" -eq 0 ]; then
|
|
||||||
if ! command -v redoxer &> /dev/null; then
|
|
||||||
echo "Error: 'redoxer' CLI not found."
|
|
||||||
echo "Please install it: cargo install redoxer"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
run_redoxer() {
|
|
||||||
export TARGET=$1
|
|
||||||
REDOXER_ENV="redoxer env"
|
|
||||||
if [ "$IS_HOST" -eq 0 ]; then
|
|
||||||
redoxer toolchain || { echo -e "${RED}Fail: redoxer toolchain for: $target.${NC}" && exit 1; }
|
|
||||||
export CARGO_TEST="redoxer"
|
|
||||||
export TEST_RUNNER="redoxer exec --folder ../../sysroot/$TARGET/:/usr --folder . --"
|
|
||||||
|
|
||||||
MAKE_ACTION="$MAKE_ACTION IS_REDOX=1"
|
|
||||||
else
|
|
||||||
REDOXER_ENV=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$TEST_BIN" != "" ]; then
|
|
||||||
if [ "$IS_HOST" -eq 0 ]; then
|
|
||||||
MAKE_ACTION="$MAKE_ACTION TESTBIN=bins_dynamic/$TEST_BIN"
|
|
||||||
else
|
|
||||||
MAKE_ACTION="$MAKE_ACTION TESTBIN=bins_static/$TEST_BIN"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$CMD_ACTION" == "make" ]; then
|
|
||||||
CMD_OPT="-j $(nproc) $MAKE_ACTION"
|
|
||||||
else
|
|
||||||
CMD_OPT="$CARGO_ACTION --target=$TARGET"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "----------------------------------------"
|
|
||||||
echo "Running $REDOXER_ENV $CMD_ACTION $CMD_OPT for: $TARGET"
|
|
||||||
|
|
||||||
if $REDOXER_ENV $CMD_ACTION $CMD_OPT; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo -e "${RED}Fail: $CMD_ACTION $CMD_OPT for $TARGET failed.${NC}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$CHECK_ALL" = true ]; then
|
|
||||||
echo "Running $CMD_ACTION for all supported Redox targets..."
|
|
||||||
|
|
||||||
has_error=false
|
|
||||||
|
|
||||||
for target in "${SUPPORTED_TARGETS[@]}"; do
|
|
||||||
if ! run_redoxer "$target"; then
|
|
||||||
has_error=true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "----------------------------------------"
|
|
||||||
if [ "$has_error" = true ]; then
|
|
||||||
echo -e "${RED}Summary: One or more targets failed.${NC}"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Summary: All targets passed!${NC}"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if run_redoxer "$CURRENT_TARGET"; then
|
|
||||||
echo -e "${GREEN}Success: $CARGO_ACTION for $CURRENT_TARGET passed.${NC}"
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
ifndef TARGET
|
|
||||||
export TARGET:=$(shell rustc -Z unstable-options --print target-spec-json | grep llvm-target | cut -d '"' -f4)
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),aarch64-unknown-linux-gnu)
|
|
||||||
export CC=aarch64-linux-gnu-gcc
|
|
||||||
export LD=aarch64-linux-gnu-ld
|
|
||||||
export AR=aarch64-linux-gnu-ar
|
|
||||||
export NM=aarch64-linux-gnu-nm
|
|
||||||
export OBJCOPY=aarch64-linux-gnu-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),aarch64-unknown-linux-relibc)
|
|
||||||
export CC=aarch64-linux-relibc-gcc
|
|
||||||
export LD=aarch64-linux-relibc-ld
|
|
||||||
export AR=aarch64-linux-relibc-ar
|
|
||||||
export NM=aarch64-linux-relibc-nm
|
|
||||||
export OBJCOPY=aarch64-linux-relibc-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
ifeq ($(TARGET),aarch64-unknown-redox)
|
|
||||||
export CC=aarch64-unknown-redox-gcc
|
|
||||||
export LD=aarch64-unknown-redox-ld
|
|
||||||
export AR=aarch64-unknown-redox-ar
|
|
||||||
export NM=aarch64-unknown-redox-nm
|
|
||||||
export OBJCOPY=aarch64-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),i586-unknown-redox)
|
|
||||||
export CC=i586-unknown-redox-gcc
|
|
||||||
export LD=i586-unknown-redox-ld
|
|
||||||
export AR=i586-unknown-redox-ar
|
|
||||||
export NM=i586-unknown-redox-nm
|
|
||||||
export OBJCOPY=i586-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),i686-unknown-redox)
|
|
||||||
export CC=i686-unknown-redox-gcc
|
|
||||||
export LD=i686-unknown-redox-ld
|
|
||||||
export AR=i686-unknown-redox-ar
|
|
||||||
export NM=i686-unknown-redox-nm
|
|
||||||
export OBJCOPY=i686-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),x86_64-unknown-linux-gnu)
|
|
||||||
export CC=x86_64-linux-gnu-gcc
|
|
||||||
export LD=x86_64-linux-gnu-ld
|
|
||||||
export AR=x86_64-linux-gnu-ar
|
|
||||||
export NM=x86_64-linux-gnu-nm
|
|
||||||
export OBJCOPY=objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld64.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),x86_64-unknown-linux-relibc)
|
|
||||||
export CC=x86_64-linux-relibc-gcc
|
|
||||||
export LD=x86_64-linux-relibc-ld
|
|
||||||
export AR=x86_64-linux-relibc-ar
|
|
||||||
export NM=x86_64-linux-relibc-nm
|
|
||||||
export OBJCOPY=x86_64-linux-relibc-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld64.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),x86_64-unknown-redox)
|
|
||||||
export CC=x86_64-unknown-redox-gcc
|
|
||||||
export LD=x86_64-unknown-redox-ld
|
|
||||||
export AR=x86_64-unknown-redox-ar
|
|
||||||
export NM=x86_64-unknown-redox-nm
|
|
||||||
export OBJCOPY=x86_64-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld64.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),riscv64gc-unknown-redox)
|
|
||||||
export CC=riscv64-unknown-redox-gcc
|
|
||||||
export LD=riscv64-unknown-redox-ld
|
|
||||||
export AR=riscv64-unknown-redox-ar
|
|
||||||
export NM=riscv64-unknown-redox-nm
|
|
||||||
export OBJCOPY=riscv64-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=-march=rv64gc -mabi=lp64d
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
# This is the default configuration file
|
||||||
|
|
||||||
|
# General settings
|
||||||
|
[general]
|
||||||
|
# Do not prompt if settings are not defined
|
||||||
|
prompt = false
|
||||||
|
|
||||||
|
# Package settings
|
||||||
|
[packages]
|
||||||
|
#acid = {}
|
||||||
|
#autoconf = {}
|
||||||
|
#automake = {}
|
||||||
|
#bash = {}
|
||||||
|
#binutils = {}
|
||||||
|
#ca-certificates = {}
|
||||||
|
#cargo = {}
|
||||||
|
#contain = {}
|
||||||
|
coreutils = {}
|
||||||
|
#curl = {}
|
||||||
|
#dash = {}
|
||||||
|
#diffutils = {}
|
||||||
|
drivers = {}
|
||||||
|
extrautils = {}
|
||||||
|
findutils = {}
|
||||||
|
#games = {}
|
||||||
|
#gawk = {}
|
||||||
|
#gcc = {}
|
||||||
|
#git = {}
|
||||||
|
#gnu-binutils = {}
|
||||||
|
#gnu-make = {}
|
||||||
|
#installer = {}
|
||||||
|
ion = {}
|
||||||
|
#lua = {}
|
||||||
|
#nasm = {}
|
||||||
|
netstack = {}
|
||||||
|
netutils = {}
|
||||||
|
#newlib = {}
|
||||||
|
#openssl = {}
|
||||||
|
orbdata = {}
|
||||||
|
orbital = {}
|
||||||
|
orbterm = {}
|
||||||
|
orbutils = {}
|
||||||
|
pastel = {}
|
||||||
|
#patch = {}
|
||||||
|
#pixelcannon = {}
|
||||||
|
pkgutils = {}
|
||||||
|
ptyd = {}
|
||||||
|
#python = {}
|
||||||
|
randd = {}
|
||||||
|
#redoxfs = {}
|
||||||
|
#rust = {}
|
||||||
|
#rustual-boy = {}
|
||||||
|
#sed = {}
|
||||||
|
smith = {}
|
||||||
|
sodium = {}
|
||||||
|
userutils = {}
|
||||||
|
uutils = {}
|
||||||
|
#xz = {}
|
||||||
|
|
||||||
|
# User settings
|
||||||
|
[users.root]
|
||||||
|
password = "password"
|
||||||
|
uid = 0
|
||||||
|
gid = 0
|
||||||
|
name = "root"
|
||||||
|
home = "/root"
|
||||||
|
|
||||||
|
[users.user]
|
||||||
|
# Password is unset
|
||||||
|
password = ""
|
||||||
|
|
||||||
|
[groups.sudo]
|
||||||
|
gid = 1
|
||||||
|
members = ["user"]
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/init.d/00_base"
|
||||||
|
data = """
|
||||||
|
pcid /etc/pcid/filesystem.toml
|
||||||
|
randd
|
||||||
|
ptyd
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/init.d/10_net"
|
||||||
|
data = """
|
||||||
|
ethernetd
|
||||||
|
ipd
|
||||||
|
icmpd
|
||||||
|
tcpd
|
||||||
|
udpd
|
||||||
|
dhcpd -b
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/init.d/20_orbital"
|
||||||
|
data = """
|
||||||
|
orbital orblogin launcher
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/init.d/30_console"
|
||||||
|
data = """
|
||||||
|
getty display/vesa:2
|
||||||
|
getty debug: -J
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/dns"
|
||||||
|
data = """
|
||||||
|
208.67.222.222
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/ip"
|
||||||
|
data = """
|
||||||
|
10.0.2.15
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/ip_router"
|
||||||
|
data = """
|
||||||
|
10.0.2.2
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/ip_subnet"
|
||||||
|
data = """
|
||||||
|
255.255.255.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/mac"
|
||||||
|
data = """
|
||||||
|
54-52-00-ab-cd-ef
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/pkg.d/50_redox"
|
||||||
|
data = "https://static.redox-os.org/pkg"
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/hostname"
|
||||||
|
data = "redox"
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/issue"
|
||||||
|
data = """
|
||||||
|
########## Redox OS ##########
|
||||||
|
# Login with the following: #
|
||||||
|
# `user` #
|
||||||
|
# `root`:`password` #
|
||||||
|
##############################
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/motd"
|
||||||
|
data = """
|
||||||
|
Welcome to Redox OS!
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr"
|
||||||
|
data = "/"
|
||||||
|
symlink = true
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/tmp"
|
||||||
|
data = ""
|
||||||
|
directory = true
|
||||||
|
# 0o1777
|
||||||
|
mode = 1023
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# This is the default configuration file
|
||||||
|
|
||||||
|
# General settings
|
||||||
|
[general]
|
||||||
|
# Do not prompt if settings are not defined
|
||||||
|
prompt = false
|
||||||
|
|
||||||
|
# Package settings
|
||||||
|
[packages]
|
||||||
|
binutils = {}
|
||||||
|
coreutils = {}
|
||||||
|
extrautils = {}
|
||||||
|
ion = {}
|
||||||
|
netutils = {}
|
||||||
|
pkgutils = {}
|
||||||
|
userutils = {}
|
||||||
|
|
||||||
|
# User settings
|
||||||
|
[users.root]
|
||||||
|
password = "password"
|
||||||
|
uid = 0
|
||||||
|
gid = 0
|
||||||
|
name = "root"
|
||||||
|
home = "/root"
|
||||||
-1
Submodule dlmalloc-rs deleted from 3b0ee3b467
-27
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
environ;
|
|
||||||
|
|
||||||
stdin;
|
|
||||||
stdout;
|
|
||||||
stderr;
|
|
||||||
|
|
||||||
timezone;
|
|
||||||
daylight;
|
|
||||||
tzname;
|
|
||||||
__timezone;
|
|
||||||
__daylight;
|
|
||||||
__tzname;
|
|
||||||
|
|
||||||
signgam;
|
|
||||||
__signgam;
|
|
||||||
|
|
||||||
optarg;
|
|
||||||
optind;
|
|
||||||
opterr;
|
|
||||||
optreset;
|
|
||||||
__optreset;
|
|
||||||
|
|
||||||
getdate_err;
|
|
||||||
|
|
||||||
__stack_chk_guard;
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cargo fmt --package relibc --package crt0 --package redox-rt "$@"
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "generic-rt"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![allow(internal_features)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use core::{
|
|
||||||
arch::asm,
|
|
||||||
mem::{self, offset_of},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct GenericTcb<Os> {
|
|
||||||
/// Pointer to the end of static TLS. Must be the first member
|
|
||||||
pub tls_end: *mut u8,
|
|
||||||
/// Size of the memory allocated for the static TLS in bytes (multiple of page size)
|
|
||||||
pub tls_len: usize,
|
|
||||||
/// Pointer to this structure
|
|
||||||
pub tcb_ptr: *mut Self,
|
|
||||||
/// Size of the memory allocated for this structure in bytes (should be same as page size)
|
|
||||||
pub tcb_len: usize,
|
|
||||||
pub os_specific: Os,
|
|
||||||
}
|
|
||||||
impl<Os> GenericTcb<Os> {
|
|
||||||
/// Architecture specific code to read a usize from the TCB - aarch64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
pub unsafe fn arch_read(offset: usize) -> usize {
|
|
||||||
let abi_ptr: usize;
|
|
||||||
asm!(
|
|
||||||
"mrs {}, tpidr_el0",
|
|
||||||
out(reg) abi_ptr,
|
|
||||||
);
|
|
||||||
|
|
||||||
let tcb_ptr = *(abi_ptr as *const usize);
|
|
||||||
*((tcb_ptr + offset) as *const usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Architecture specific code to read a usize from the TCB - x86
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub unsafe fn arch_read(offset: usize) -> usize {
|
|
||||||
let value;
|
|
||||||
asm!(
|
|
||||||
"
|
|
||||||
mov {}, gs:[{}]
|
|
||||||
",
|
|
||||||
out(reg) value,
|
|
||||||
in(reg) offset,
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Architecture specific code to read a usize from the TCB - x86_64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub unsafe fn arch_read(offset: usize) -> usize {
|
|
||||||
let value;
|
|
||||||
asm!(
|
|
||||||
"
|
|
||||||
mov {}, fs:[{}]
|
|
||||||
",
|
|
||||||
out(reg) value,
|
|
||||||
in(reg) offset,
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Architecture specific code to read a usize from the TCB - riscv64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
unsafe fn arch_read(offset: usize) -> usize {
|
|
||||||
let value;
|
|
||||||
asm!(
|
|
||||||
"ld {value}, -8(tp)", // TCB
|
|
||||||
"add {value}, {value}, {offset}",
|
|
||||||
"ld {value}, 0({value})",
|
|
||||||
value = out(reg) value,
|
|
||||||
offset = in(reg) offset,
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn current_ptr() -> Option<*mut Self> {
|
|
||||||
let tcb_ptr = unsafe { Self::arch_read(offset_of!(Self, tcb_ptr)) as *mut Self };
|
|
||||||
let tcb_len = unsafe { Self::arch_read(offset_of!(Self, tcb_len)) };
|
|
||||||
if tcb_ptr.is_null() || tcb_len < mem::size_of::<Self>() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(tcb_ptr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub unsafe fn current() -> Option<&'static mut Self> {
|
|
||||||
unsafe { Some(&mut *Self::current_ptr()?) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
Generated
+7016
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
name = "redox_installer_gui"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1"
|
||||||
|
pkgar = "0.2"
|
||||||
|
pkgar-core = "0.2"
|
||||||
|
pkgar-keys = "0.2"
|
||||||
|
redox_installer = { path = ".." }
|
||||||
|
redox_syscall = "0.9"
|
||||||
|
toml = "0.8"
|
||||||
|
# TODO: remove after pkgutils update
|
||||||
|
futures-channel = "0.3.32"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "redox")'.dependencies]
|
||||||
|
libredox = "0.1"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
[dependencies.libcosmic]
|
||||||
|
git = "https://github.com/pop-os/libcosmic.git"
|
||||||
|
# use the same rev with other cosmic app
|
||||||
|
rev = "bb10afd849f1ad172be6b78eeec347d548aa4fd7"
|
||||||
|
default-features = false
|
||||||
|
features = ["winit"]
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
ring = { git = "https://gitlab.redox-os.org/redox-os/ring.git", branch = "redox-0.17.8" }
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# installer_gui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||||
|
|
||||||
|
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||||
|
|
||||||
|
## Add your files
|
||||||
|
|
||||||
|
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||||
|
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd existing_repo
|
||||||
|
git remote add origin https://gitlab.redox-os.org/redox-os/installer_gui.git
|
||||||
|
git branch -M main
|
||||||
|
git push -uf origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integrate with your tools
|
||||||
|
|
||||||
|
- [ ] [Set up project integrations](https://gitlab.redox-os.org/redox-os/installer_gui/-/settings/integrations)
|
||||||
|
|
||||||
|
## Collaborate with your team
|
||||||
|
|
||||||
|
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||||
|
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||||
|
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||||
|
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||||
|
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
||||||
|
|
||||||
|
## Test and Deploy
|
||||||
|
|
||||||
|
Use the built-in continuous integration in GitLab.
|
||||||
|
|
||||||
|
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
||||||
|
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||||
|
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||||
|
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||||
|
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
# Editing this README
|
||||||
|
|
||||||
|
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||||
|
|
||||||
|
## Suggestions for a good README
|
||||||
|
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||||
|
|
||||||
|
## Name
|
||||||
|
Choose a self-explaining name for your project.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||||
|
|
||||||
|
## Badges
|
||||||
|
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||||
|
|
||||||
|
## Visuals
|
||||||
|
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
State if you are open to contributions and what your requirements are for accepting them.
|
||||||
|
|
||||||
|
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||||
|
|
||||||
|
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||||
|
|
||||||
|
## Authors and acknowledgment
|
||||||
|
Show your appreciation to those who have contributed to the project.
|
||||||
|
|
||||||
|
## License
|
||||||
|
For open source projects, say how it is licensed.
|
||||||
|
|
||||||
|
## Project status
|
||||||
|
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||||
+634
@@ -0,0 +1,634 @@
|
|||||||
|
use anyhow::format_err;
|
||||||
|
use cosmic::{
|
||||||
|
app::{self, Task},
|
||||||
|
iced::{
|
||||||
|
self, executor,
|
||||||
|
futures::sink::SinkExt,
|
||||||
|
widget::{row, text_input},
|
||||||
|
window, Alignment, Size, Subscription,
|
||||||
|
},
|
||||||
|
widget::{button, column, progress_bar, radio, space, text},
|
||||||
|
Application, ApplicationExt, Core, Element,
|
||||||
|
};
|
||||||
|
use futures_channel::mpsc;
|
||||||
|
use pkgar::{ext::EntryExt, PackageHead};
|
||||||
|
use pkgar_core::PackageSrc;
|
||||||
|
use pkgar_keys::PublicKeyFile;
|
||||||
|
use redox_installer::{try_fast_install, with_redoxfs_mount, with_whole_disk, Config, DiskOption};
|
||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
fs,
|
||||||
|
io::{self, Read, Write},
|
||||||
|
os::unix::fs::{symlink, MetadataExt, OpenOptionsExt},
|
||||||
|
path::Path,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod sys;
|
||||||
|
pub use sys::*;
|
||||||
|
|
||||||
|
fn main() -> iced::Result {
|
||||||
|
let mut settings = app::Settings::default();
|
||||||
|
settings = settings.size(Size::new(608.0, 416.0));
|
||||||
|
settings = settings.exit_on_close(false);
|
||||||
|
app::run::<Window>(settings, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> anyhow::Result<()> {
|
||||||
|
if let Some(parent) = dest.parent() {
|
||||||
|
// Parent may be a symlink
|
||||||
|
if !parent.is_symlink() {
|
||||||
|
match fs::create_dir_all(&parent) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"failed to create directory {}: {}",
|
||||||
|
parent.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let metadata = match fs::symlink_metadata(&src) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"failed to read metadata of {}: {}",
|
||||||
|
src.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if metadata.file_type().is_symlink() {
|
||||||
|
let real_src = match fs::read_link(&src) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"failed to read link {}: {}",
|
||||||
|
src.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match symlink(&real_src, &dest) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"failed to copy link {} ({}) to {}: {}",
|
||||||
|
src.display(),
|
||||||
|
real_src.display(),
|
||||||
|
dest.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut src_file = match fs::File::open(&src) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"failed to open file {}: {}",
|
||||||
|
src.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut dest_file = match fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.mode(metadata.mode())
|
||||||
|
.open(&dest)
|
||||||
|
{
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"failed to create file {}: {}",
|
||||||
|
dest.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let count = match src_file.read(buf) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"failed to read file {}: {}",
|
||||||
|
src.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match dest_file.write_all(&buf[..count]) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"failed to write file {}: {}",
|
||||||
|
dest.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn package_files(
|
||||||
|
root_path: &Path,
|
||||||
|
config: &mut Config,
|
||||||
|
files: &mut Vec<String>,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
//TODO: Remove packages from config where all files are located (and have valid shasum?)
|
||||||
|
config.packages.clear();
|
||||||
|
|
||||||
|
let pkey_path = "pkg/id_ed25519.pub.toml";
|
||||||
|
let pkey = PublicKeyFile::open(&root_path.join(pkey_path))?.pkey;
|
||||||
|
files.push(pkey_path.to_string());
|
||||||
|
|
||||||
|
for item_res in fs::read_dir(&root_path.join("pkg"))? {
|
||||||
|
let item = item_res?;
|
||||||
|
let pkg_path = item.path();
|
||||||
|
if pkg_path.extension() == Some(OsStr::new("pkgar_head")) {
|
||||||
|
let mut pkg = PackageHead::new(&pkg_path, &root_path, &pkey)?;
|
||||||
|
for entry in pkg.read_entries()? {
|
||||||
|
files.push(entry.check_path()?.to_str().unwrap().to_string());
|
||||||
|
}
|
||||||
|
files.push(
|
||||||
|
pkg_path
|
||||||
|
.strip_prefix(root_path)
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install<F: FnMut(Message)>(disk_path: String, password_opt: Option<String>, mut f: F) {
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
|
let mut progress = 0;
|
||||||
|
|
||||||
|
macro_rules! message {
|
||||||
|
($($arg:tt)*) => {{
|
||||||
|
eprintln!($($arg)*);
|
||||||
|
f(Message::Install(
|
||||||
|
progress,
|
||||||
|
format!($($arg)*)
|
||||||
|
));
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
let root_path = Path::new("/scheme/file/");
|
||||||
|
|
||||||
|
message!("Loading bootloader");
|
||||||
|
let bootloader_bios = {
|
||||||
|
let path = root_path.join("boot").join("bootloader.bios");
|
||||||
|
if path.exists() {
|
||||||
|
match fs::read(&path) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
f(Message::Error(format!(
|
||||||
|
"{}: failed to read: {}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
message!("Loading bootloader.efi");
|
||||||
|
let bootloader_efi = {
|
||||||
|
let path = root_path.join("boot").join("bootloader.efi");
|
||||||
|
if path.exists() {
|
||||||
|
match fs::read(&path) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
f(Message::Error(format!(
|
||||||
|
"{}: failed to read: {}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
message!("Formatting disk");
|
||||||
|
let disk_option = DiskOption {
|
||||||
|
bootloader_bios: &bootloader_bios,
|
||||||
|
bootloader_efi: &bootloader_efi,
|
||||||
|
password_opt: password_opt.as_ref().map(|x| x.as_bytes()),
|
||||||
|
efi_partition_size: None,
|
||||||
|
skip_partitions: false,
|
||||||
|
};
|
||||||
|
let res = with_whole_disk(&disk_path, &disk_option, |mut fs| -> anyhow::Result<()> {
|
||||||
|
// Fast install method via filesystem clone
|
||||||
|
let mut last_progress = 0;
|
||||||
|
if try_fast_install(&mut fs, |used, used_old| {
|
||||||
|
progress = ((used * 100) / used_old) as usize;
|
||||||
|
if progress != last_progress {
|
||||||
|
message!(
|
||||||
|
"{}%: {} MB/{} MB",
|
||||||
|
progress,
|
||||||
|
used / 1000 / 1000,
|
||||||
|
used_old / 1000 / 1000
|
||||||
|
);
|
||||||
|
last_progress = progress;
|
||||||
|
}
|
||||||
|
})? {
|
||||||
|
progress = 100;
|
||||||
|
message!("Finished installing using fast mode");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
with_redoxfs_mount(fs, None, |mount_path: &Path| -> anyhow::Result<()> {
|
||||||
|
message!("Loading filesystem.toml");
|
||||||
|
let mut config: Config = {
|
||||||
|
let path = root_path.join("filesystem.toml");
|
||||||
|
match fs::read_to_string(&path) {
|
||||||
|
Ok(config_data) => match toml::from_str(&config_data) {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!(
|
||||||
|
"{}: failed to decode: {}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format_err!("{}: failed to read: {}", path.display(), err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy filesystem.toml, which is not packaged
|
||||||
|
let mut files = vec!["filesystem.toml".to_string()];
|
||||||
|
|
||||||
|
// Copy files from locally installed packages
|
||||||
|
message!("Loading package files");
|
||||||
|
if let Err(err) = package_files(&root_path, &mut config, &mut files) {
|
||||||
|
return Err(format_err!("failed to read package files: {}", err));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort and remove duplicates
|
||||||
|
files.sort();
|
||||||
|
files.dedup();
|
||||||
|
|
||||||
|
// Perform config install (after packages have been converted to files)
|
||||||
|
message!("Configuring system");
|
||||||
|
let cookbook: Option<&'static str> = None;
|
||||||
|
redox_installer::install_dir(config, mount_path, cookbook)
|
||||||
|
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||||
|
|
||||||
|
// Install files
|
||||||
|
let mut buf = vec![0; 4096 * 1024];
|
||||||
|
for (i, name) in files.iter().enumerate() {
|
||||||
|
progress = (i * 100) / files.len();
|
||||||
|
message!("Copy {} [{}/{}]", name, i, files.len());
|
||||||
|
|
||||||
|
let src = root_path.join(name);
|
||||||
|
let dest = mount_path.join(name);
|
||||||
|
copy_file(&src, &dest, &mut buf)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress = 100;
|
||||||
|
message!("Finished installing, unmounting filesystem");
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(()) => {
|
||||||
|
f(Message::Success(format!(
|
||||||
|
"Finished installing in {:?}, ready to reboot",
|
||||||
|
start.elapsed()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
f(Message::Error(format!("Failed to install: {}", err)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Page {
|
||||||
|
Sudo(String),
|
||||||
|
Disk(Option<usize>),
|
||||||
|
Install(usize, String),
|
||||||
|
Success(String),
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Worker {
|
||||||
|
command_sender: std::sync::mpsc::Sender<(String, Option<String>)>,
|
||||||
|
join_handle: Arc<std::thread::JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum Message {
|
||||||
|
None,
|
||||||
|
Worker(Worker),
|
||||||
|
SudoInput(String),
|
||||||
|
SudoSubmit,
|
||||||
|
DiskChoose(usize),
|
||||||
|
DiskConfirm(usize),
|
||||||
|
Install(usize, String),
|
||||||
|
Success(String),
|
||||||
|
Exit,
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Window {
|
||||||
|
core: Core,
|
||||||
|
page: Page,
|
||||||
|
disk_paths: Vec<(String, bool, u64)>,
|
||||||
|
worker_opt: Option<Worker>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
Ready,
|
||||||
|
Waiting(mpsc::UnboundedReceiver<Message>),
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
fn worker_stream() -> impl iced::futures::Stream<Item = Message> {
|
||||||
|
iced::stream::channel(100, |mut output: mpsc::Sender<Message>| async move {
|
||||||
|
let mut state = State::Ready;
|
||||||
|
loop {
|
||||||
|
let (message, new_state) = match state {
|
||||||
|
State::Ready => {
|
||||||
|
let (command_sender, command_receiver) = std::sync::mpsc::channel();
|
||||||
|
|
||||||
|
let (message_sender, message_receiver) = mpsc::unbounded();
|
||||||
|
|
||||||
|
//TODO: kill worker thread?
|
||||||
|
let join_handle = std::thread::spawn(move || {
|
||||||
|
while let Ok((disk_path, password_opt)) = command_receiver.recv() {
|
||||||
|
println!("Installing to {:?}", disk_path);
|
||||||
|
install(disk_path, password_opt, |message| {
|
||||||
|
message_sender.unbounded_send(message).unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let worker = Worker {
|
||||||
|
command_sender,
|
||||||
|
join_handle: std::sync::Arc::new(join_handle),
|
||||||
|
};
|
||||||
|
|
||||||
|
(Message::Worker(worker), State::Waiting(message_receiver))
|
||||||
|
}
|
||||||
|
State::Waiting(mut message_receiver) => {
|
||||||
|
use iced::futures::StreamExt;
|
||||||
|
match message_receiver.next().await {
|
||||||
|
Some(message) => (message, State::Waiting(message_receiver)),
|
||||||
|
None => (Message::None, State::Finished),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Finished => iced::futures::future::pending().await,
|
||||||
|
};
|
||||||
|
output.send(message).await.unwrap();
|
||||||
|
state = new_state;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Application for Window {
|
||||||
|
type Executor = executor::Default;
|
||||||
|
type Flags = ();
|
||||||
|
type Message = Message;
|
||||||
|
|
||||||
|
const APP_ID: &'static str = "org.redox-os.InstallerGui";
|
||||||
|
|
||||||
|
fn init(core: Core, _flags: ()) -> (Self, Task<Message>) {
|
||||||
|
//TODO: load in background
|
||||||
|
let (page, disk_paths) = match disk_paths() {
|
||||||
|
Ok(disk_paths) => (Page::Disk(None), disk_paths),
|
||||||
|
Err(err) => (Page::Error(err), Vec::new()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut app = Self {
|
||||||
|
core,
|
||||||
|
page,
|
||||||
|
disk_paths,
|
||||||
|
worker_opt: None,
|
||||||
|
};
|
||||||
|
let task = app.set_window_title("Redox OS Installer".to_string());
|
||||||
|
(app, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn core(&self) -> &Core {
|
||||||
|
&self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn core_mut(&mut self) -> &mut Core {
|
||||||
|
&mut self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
|
match message {
|
||||||
|
Message::None => {}
|
||||||
|
Message::Worker(worker) => {
|
||||||
|
self.worker_opt = Some(worker);
|
||||||
|
}
|
||||||
|
Message::SudoInput(password) => {
|
||||||
|
self.page = Page::Sudo(password);
|
||||||
|
}
|
||||||
|
Message::SudoSubmit => {
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
if let Page::Sudo(password) = &self.page {
|
||||||
|
match ask_root(&password) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.page = Page::Disk(None);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
match ask_root() {
|
||||||
|
Ok(()) => {
|
||||||
|
if let Some(window_id) = self.core.main_window_id() {
|
||||||
|
return window::close(window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::DiskChoose(disk_i) => {
|
||||||
|
self.page = Page::Disk(Some(disk_i));
|
||||||
|
}
|
||||||
|
Message::DiskConfirm(disk_i) => match self.disk_paths.get(disk_i) {
|
||||||
|
Some((disk_path, _is_partition, _disk_size)) => match &self.worker_opt {
|
||||||
|
Some(worker) => match worker.command_sender.send((disk_path.clone(), None)) {
|
||||||
|
Ok(()) => self.page = Page::Install(0, format!("Starting install...")),
|
||||||
|
Err(err) => {
|
||||||
|
self.page = Page::Error(format!("failed to send command: {}", err));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
self.page = Page::Error(format!("command sender not found"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
self.page = Page::Error(format!("invalid disk number {} chosen", disk_i));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Message::Install(progress, description) => {
|
||||||
|
self.page = Page::Install(progress, description);
|
||||||
|
}
|
||||||
|
Message::Success(description) => {
|
||||||
|
self.page = Page::Success(description);
|
||||||
|
}
|
||||||
|
Message::Error(err) => {
|
||||||
|
self.page = Page::Error(err);
|
||||||
|
}
|
||||||
|
Message::Exit => {
|
||||||
|
if let Some(worker) = self.worker_opt.take() {
|
||||||
|
drop(worker.command_sender);
|
||||||
|
let join_handle = Arc::try_unwrap(worker.join_handle).unwrap();
|
||||||
|
join_handle.join().unwrap();
|
||||||
|
}
|
||||||
|
if let Some(window_id) = self.core.main_window_id() {
|
||||||
|
return window::close(window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Element<'_, Message> {
|
||||||
|
let mut widgets = Vec::new();
|
||||||
|
match &self.page {
|
||||||
|
Page::Sudo(password) => {
|
||||||
|
widgets.push(text("Enter your password:").into());
|
||||||
|
widgets.push(
|
||||||
|
text_input("", password)
|
||||||
|
.on_input(Message::SudoInput)
|
||||||
|
.secure(true)
|
||||||
|
.on_submit(Message::SudoSubmit)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Page::Disk(disk_i_opt) => {
|
||||||
|
if !self.disk_paths.is_empty() {
|
||||||
|
if is_root() {
|
||||||
|
widgets.push(text("Choose a drive:").size(24).into());
|
||||||
|
|
||||||
|
for (disk_i, (disk_path, is_partition, disk_size)) in
|
||||||
|
self.disk_paths.iter().enumerate()
|
||||||
|
{
|
||||||
|
if !is_partition {
|
||||||
|
widgets.push(
|
||||||
|
row![
|
||||||
|
radio(
|
||||||
|
text(disk_path),
|
||||||
|
disk_i,
|
||||||
|
*disk_i_opt,
|
||||||
|
Message::DiskChoose
|
||||||
|
),
|
||||||
|
space::horizontal(),
|
||||||
|
text(redox_installer::format_bytes(*disk_size)),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(disk_i) = *disk_i_opt {
|
||||||
|
widgets.push(space::vertical().into());
|
||||||
|
widgets.push(
|
||||||
|
row![
|
||||||
|
space::horizontal(),
|
||||||
|
button::destructive("Confirm")
|
||||||
|
.on_press(Message::DiskConfirm(disk_i)),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
let page = Message::SudoSubmit;
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
let page = Message::SudoInput(String::new());
|
||||||
|
|
||||||
|
widgets.push(space::vertical().into());
|
||||||
|
widgets.push(
|
||||||
|
row![
|
||||||
|
text("Ask superuser permission to install into drives"),
|
||||||
|
space::horizontal(),
|
||||||
|
button::suggested("Ask root access").on_press(page),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
widgets.push(text("No drives found").into());
|
||||||
|
// TODO: expose disk.pci-*-*nvme/* */ scheme to user
|
||||||
|
widgets.push(text("(try to rerun with sudo)").into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Page::Install(progress, description) => {
|
||||||
|
widgets.push(text("Installation progress:").size(24).into());
|
||||||
|
widgets.push(progress_bar::determinate_linear(*progress as f32 / 100.).into());
|
||||||
|
widgets.push(text(description).into());
|
||||||
|
}
|
||||||
|
Page::Success(description) => {
|
||||||
|
widgets.push(text("Installation complete!").size(24).into());
|
||||||
|
widgets.push(text(description).into());
|
||||||
|
widgets.push(space::vertical().into());
|
||||||
|
widgets.push(
|
||||||
|
row![
|
||||||
|
space::horizontal(),
|
||||||
|
button::standard("Exit").on_press(Message::Exit),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Page::Error(err) => {
|
||||||
|
widgets.push(text(format!("{}", err)).into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
column::with_children(widgets)
|
||||||
|
.spacing(8)
|
||||||
|
.padding(24)
|
||||||
|
.align_x(Alignment::Start)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
|
Subscription::run(Self::worker_stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod linux;
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
mod redox;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub use linux::*;
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
pub use redox::*;
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "redox")))]
|
||||||
|
compile_error!("Platform is not ported");
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
use std::{fs, os::unix::process::CommandExt, process::Command};
|
||||||
|
|
||||||
|
pub fn ask_root() -> Result<(), String> {
|
||||||
|
let Ok(exe_path) = std::env::current_exe() else {
|
||||||
|
return Err(format!("Could not determine current_exe"));
|
||||||
|
};
|
||||||
|
let mut cmd = Command::new("pkexec");
|
||||||
|
cmd.arg("env");
|
||||||
|
for (key, value) in std::env::vars() {
|
||||||
|
if [
|
||||||
|
"DISPLAY",
|
||||||
|
"WAYLAND_DISPLAY",
|
||||||
|
"XAUTHORITY",
|
||||||
|
"XDG_RUNTIME_DIR",
|
||||||
|
"DBUS_SESSION_BUS_ADDRESS",
|
||||||
|
]
|
||||||
|
.contains(&key.as_str())
|
||||||
|
{
|
||||||
|
cmd.arg(format!("{}={}", key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.arg(exe_path);
|
||||||
|
// will never return unless fail
|
||||||
|
let e = cmd.exec();
|
||||||
|
return Err(format!("Failed to escalate: {e}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_root() -> bool {
|
||||||
|
let euid = unsafe { libc::geteuid() };
|
||||||
|
euid == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_paths() -> Result<Vec<(String, bool, u64)>, String> {
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
|
||||||
|
let entries = match fs::read_dir("/sys/class/block") {
|
||||||
|
Ok(entries) => entries,
|
||||||
|
Err(err) => return Err(format!("failed to read /sys/class/block: {}", err)),
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry_res in entries {
|
||||||
|
if let Ok(entry) = entry_res {
|
||||||
|
let file_name = entry.file_name();
|
||||||
|
let name = file_name.to_string_lossy();
|
||||||
|
|
||||||
|
if name.starts_with("loop")
|
||||||
|
|| name.starts_with("ram")
|
||||||
|
|| name.starts_with("sr")
|
||||||
|
|| name.starts_with("zram")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = entry.path();
|
||||||
|
let is_partition = path.join("partition").exists();
|
||||||
|
let size_path = path.join("size");
|
||||||
|
let Ok(size_str) = fs::read_to_string(&size_path) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(sectors) = size_str.trim().parse::<u64>() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
// /sys/class/block/*/size is 512 (cmiiw)
|
||||||
|
let size_bytes = sectors * 512;
|
||||||
|
if size_bytes == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
paths.push((format!("/dev/{}", name), is_partition, size_bytes));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paths.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
|
|
||||||
|
Ok(paths)
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
pub fn ask_root(password: &str) -> Result<(), String> {
|
||||||
|
let file = libredox::Fd::open("/scheme/sudo", libredox::flag::O_CLOEXEC, 0)
|
||||||
|
.map_err(|err| err.to_string())?;
|
||||||
|
|
||||||
|
file.write(password.as_bytes())
|
||||||
|
.map_err(|err| err.to_string())?;
|
||||||
|
|
||||||
|
// FIXME move to libredox
|
||||||
|
unsafe extern "C" {
|
||||||
|
safe fn redox_cur_procfd_v0() -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elevate privileges of our own process with help from the sudo daemon
|
||||||
|
file.call_wo(
|
||||||
|
&libredox::call::dup(redox_cur_procfd_v0(), &[])
|
||||||
|
.map_err(|err| err.to_string())?
|
||||||
|
.to_ne_bytes(),
|
||||||
|
syscall::CallFlags::FD,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.map_err(|err| err.to_string())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_root() -> bool {
|
||||||
|
let euid = libredox::call::geteuid().unwrap();
|
||||||
|
euid == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_paths() -> Result<Vec<(String, bool, u64)>, String> {
|
||||||
|
let mut schemes = Vec::new();
|
||||||
|
match fs::read_dir("/scheme/") {
|
||||||
|
Ok(entries) => {
|
||||||
|
for entry_res in entries {
|
||||||
|
if let Ok(entry) = entry_res {
|
||||||
|
let path = entry.path();
|
||||||
|
if let Ok(path_str) = path.into_os_string().into_string() {
|
||||||
|
let scheme = path_str.trim_start_matches("/scheme/").trim_matches('/');
|
||||||
|
if scheme.starts_with("disk") {
|
||||||
|
if scheme == "disk/live" {
|
||||||
|
// Skip live disks
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
schemes.push(format!("/scheme/{}", scheme));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format!("failed to list schemes: {}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
for scheme in schemes {
|
||||||
|
let is_dir = fs::metadata(&scheme).map(|x| x.is_dir()).unwrap_or(false);
|
||||||
|
if !is_dir {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match fs::read_dir(&scheme) {
|
||||||
|
Ok(entries) => {
|
||||||
|
for entry_res in entries {
|
||||||
|
let Ok(entry) = entry_res else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(file_name) = entry.file_name().into_string() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(path) = entry.path().into_os_string().into_string() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(metadata) = entry.metadata() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let is_partition = file_name.contains('p');
|
||||||
|
let size = metadata.len();
|
||||||
|
if size == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
paths.push((path, is_partition, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format!("failed to list {:?}: {}", scheme, err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(paths)
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef _ALLOCA_H
|
|
||||||
#define _ALLOCA_H
|
|
||||||
|
|
||||||
#define alloca(size) __builtin_alloca (size)
|
|
||||||
|
|
||||||
#endif /* _ALLOCA_H */
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/* ar.h — Unix archive (ar) file format.
|
|
||||||
*
|
|
||||||
* Red Bear OS / relibc. Defines the archive magic strings and per-member header
|
|
||||||
* layout used by `ar(1)` archives (and consumed by libelf's archive support).
|
|
||||||
* Header-only: purely a struct and string constants, no runtime symbols.
|
|
||||||
*/
|
|
||||||
#ifndef _AR_H
|
|
||||||
#define _AR_H 1
|
|
||||||
|
|
||||||
#define ARMAG "!<arch>\n" /* String that begins an archive file. */
|
|
||||||
#define SARMAG 8 /* Size of ARMAG string. */
|
|
||||||
#define ARFMAG "`\n" /* String at the end of each header. */
|
|
||||||
|
|
||||||
struct ar_hdr {
|
|
||||||
char ar_name[16]; /* Member file name, blank/`/`-terminated. */
|
|
||||||
char ar_date[12]; /* File date, decimal seconds since epoch. */
|
|
||||||
char ar_uid[6]; /* User id, in ASCII decimal. */
|
|
||||||
char ar_gid[6]; /* Group id, in ASCII decimal. */
|
|
||||||
char ar_mode[8]; /* File mode, in ASCII octal. */
|
|
||||||
char ar_size[10]; /* File size, in ASCII decimal. */
|
|
||||||
char ar_fmag[2]; /* Always contains ARFMAG. */
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* ar.h */
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/* byteswap.h — byte-order swapping macros.
|
|
||||||
*
|
|
||||||
* Red Bear OS / relibc. Mirrors the glibc <byteswap.h> interface. The macros
|
|
||||||
* lower to the compiler byte-swap builtins, so they are constant-foldable and
|
|
||||||
* need no runtime symbol. Provided because portable ELF/object tooling
|
|
||||||
* (elfutils/libelf, etc.) uses bswap_16/32/64 directly.
|
|
||||||
*/
|
|
||||||
#ifndef _BYTESWAP_H
|
|
||||||
#define _BYTESWAP_H 1
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define bswap_16(x) __builtin_bswap16((uint16_t)(x))
|
|
||||||
#define bswap_32(x) __builtin_bswap32((uint32_t)(x))
|
|
||||||
#define bswap_64(x) __builtin_bswap64((uint64_t)(x))
|
|
||||||
|
|
||||||
#endif /* byteswap.h */
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <openlibm_complex.h>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/* error.h — GNU-style error reporting.
|
|
||||||
*
|
|
||||||
* Red Bear OS / relibc. Mirrors glibc <error.h>: error() and error_at_line()
|
|
||||||
* print the program name (best effort), a formatted message, and optionally a
|
|
||||||
* strerror() of errnum to stderr, then exit() when status is non-zero.
|
|
||||||
*
|
|
||||||
* These are DECLARED here and IMPLEMENTED as real exported symbols in relibc
|
|
||||||
* (src/header/error). They must NOT be static-inline: autotools/gnulib link-test
|
|
||||||
* for a system error() and, finding one, skip compiling their own lib/error.c —
|
|
||||||
* a header-only inline collides with that replacement ("redefinition of
|
|
||||||
* 'error'", seen building m4/gnulib).
|
|
||||||
*/
|
|
||||||
#ifndef _ERROR_H
|
|
||||||
#define _ERROR_H 1
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Count of messages printed by error()/error_at_line(). */
|
|
||||||
extern unsigned int error_message_count;
|
|
||||||
|
|
||||||
/* When non-zero, error_at_line() prints each distinct file:line only once. */
|
|
||||||
extern int error_one_per_line;
|
|
||||||
|
|
||||||
/* Optional hook to print the program name instead of the default. */
|
|
||||||
extern void (*error_print_progname)(void);
|
|
||||||
|
|
||||||
extern void error(int __status, int __errnum, const char *__format, ...)
|
|
||||||
__attribute__((__format__(__printf__, 3, 4)));
|
|
||||||
|
|
||||||
extern void error_at_line(int __status, int __errnum, const char *__fname,
|
|
||||||
unsigned int __lineno, const char *__format, ...)
|
|
||||||
__attribute__((__format__(__printf__, 5, 6)));
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* error.h */
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
* Copyright (c) 2020 Rich Felker musl-libc
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _FEATURES_H__RELIBC
|
|
||||||
#define _FEATURES_H__RELIBC
|
|
||||||
|
|
||||||
// Version metadata for feature gating
|
|
||||||
// This is useful for divergent implementation specific behavior
|
|
||||||
// glibc, ulibc, and likely others define a similar macro
|
|
||||||
// musl does not define an equivalent macro
|
|
||||||
#define __RELIBC__ 1
|
|
||||||
#define __RELIBC__MAJOR 0
|
|
||||||
#define __RELIBC__MINOR 2
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sources:
|
|
||||||
* https://en.cppreference.com/w/c/language/attributes
|
|
||||||
* https://clang.llvm.org/docs/LanguageExtensions.html
|
|
||||||
* https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005fc_005fattribute.html
|
|
||||||
* https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Clang doesn't define __has_cpp_attribute if compiling C code
|
|
||||||
#if !defined(__has_cpp_attribute)
|
|
||||||
#define __has_cpp_attribute(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Clang doesn't define __has_c_attribute if compiling C++ code
|
|
||||||
#if !defined(__has_c_attribute)
|
|
||||||
#define __has_c_attribute(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Check if C23+ attributes are available
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
// HACK: GCC backports C++ attributes to C++98 but doesn't accept attributes
|
|
||||||
// placed before the function like cbindgen emits.
|
|
||||||
// Let's just disable attributes for C++98 by checking if a random C++11
|
|
||||||
// feature is available.
|
|
||||||
#define __HAS_ATTRIBUTE(x) __cpp_variable_templates &&__has_cpp_attribute(x)
|
|
||||||
#else
|
|
||||||
#define __HAS_ATTRIBUTE(x) \
|
|
||||||
(__has_c_attribute(x) || __STDC_VERSION__ >= 202311L || \
|
|
||||||
__has_cpp_attribute(x))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// From musl, license MIT {
|
|
||||||
#if defined(_ALL_SOURCE) && !defined(_GNU_SOURCE)
|
|
||||||
#define _GNU_SOURCE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_DEFAULT_SOURCE) && !defined(_BSD_SOURCE)
|
|
||||||
#define _BSD_SOURCE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(_POSIX_SOURCE) && !defined(_POSIX_C_SOURCE) \
|
|
||||||
&& !defined(_XOPEN_SOURCE) && !defined(_GNU_SOURCE) \
|
|
||||||
&& !defined(_BSD_SOURCE) && !defined(__STRICT_ANSI__)
|
|
||||||
#define _BSD_SOURCE 1
|
|
||||||
#define _XOPEN_SOURCE 700
|
|
||||||
#endif
|
|
||||||
// } from musl
|
|
||||||
|
|
||||||
// TODO: Not emitted with cbindgen
|
|
||||||
#if __STDC_VERSION__ >= 199901L
|
|
||||||
#define __restrict restrict
|
|
||||||
#elif !defined(__GNUC__)
|
|
||||||
#define __restrict
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO: Not emitted with cbindgen
|
|
||||||
#if __STDC_VERSION__ >= 199901L || defined(__cplusplus)
|
|
||||||
#define __inline inline
|
|
||||||
#elif !defined(__GNUC__)
|
|
||||||
#define __inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Analogous to Rust's Never type
|
|
||||||
//TODO: clang fails to compile C with [[noreturn]]
|
|
||||||
#if defined(__cplusplus) && __HAS_ATTRIBUTE(noreturn) && !(__clang__)
|
|
||||||
#define __noreturn [[noreturn]]
|
|
||||||
// #elif __STDC_VERSION__ >= 201112L
|
|
||||||
// FIXME: cbindgen incorrectly places _Noreturn
|
|
||||||
// #define __noreturn _Noreturn
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#define __noreturn __attribute__((__noreturn__))
|
|
||||||
#else
|
|
||||||
#define __noreturn
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Analogous to Rust's #[must_use]
|
|
||||||
// C23 only
|
|
||||||
#if __HAS_ATTRIBUTE(nodiscard)
|
|
||||||
#define __nodiscard [[nodiscard]]
|
|
||||||
#define __nodiscardNote(x) [[nodiscard(x)]]
|
|
||||||
#else
|
|
||||||
#define __nodiscard
|
|
||||||
#define __nodiscardNote(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Analogous to Rust's #[deprecated]
|
|
||||||
// C23 only
|
|
||||||
#if __HAS_ATTRIBUTE(deprecated)
|
|
||||||
#define __deprecated [[deprecated]]
|
|
||||||
#define __deprecatedNote(x) [[deprecated(x)]]
|
|
||||||
#else
|
|
||||||
#define __deprecated
|
|
||||||
#define __deprecatedNote(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#include <openlibm_fenv.h>
|
|
||||||
#undef complex
|
|
||||||
#undef I
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Copied from musl
|
|
||||||
|
|
||||||
#ifndef _ISO646_H
|
|
||||||
#define _ISO646_H
|
|
||||||
|
|
||||||
#ifndef __cplusplus
|
|
||||||
|
|
||||||
#define and &&
|
|
||||||
#define and_eq &=
|
|
||||||
#define bitand &
|
|
||||||
#define bitor |
|
|
||||||
#define compl ~
|
|
||||||
#define not !
|
|
||||||
#define not_eq !=
|
|
||||||
#define or ||
|
|
||||||
#define or_eq |=
|
|
||||||
#define xor ^
|
|
||||||
#define xor_eq ^=
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/* libintl.h — message translation (gettext) interface.
|
|
||||||
*
|
|
||||||
* Red Bear OS / relibc. Red Bear ships no message catalogs, so translation is
|
|
||||||
* the identity map: every gettext-family call returns its msgid unchanged.
|
|
||||||
* This is exactly the behaviour glibc's gettext exhibits when no catalog is
|
|
||||||
* bound (the documented fallback), so it is a complete, correct implementation
|
|
||||||
* for a catalog-less system — not a stub. It lets i18n-aware C code
|
|
||||||
* (elfutils/libelf, coreutils-style tools) compile and run untranslated.
|
|
||||||
*/
|
|
||||||
#ifndef _LIBINTL_H
|
|
||||||
#define _LIBINTL_H 1
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline char *gettext(const char *__msgid) {
|
|
||||||
return (char *)__msgid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *dgettext(const char *__domainname, const char *__msgid) {
|
|
||||||
(void)__domainname;
|
|
||||||
return (char *)__msgid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *dcgettext(const char *__domainname, const char *__msgid,
|
|
||||||
int __category) {
|
|
||||||
(void)__domainname;
|
|
||||||
(void)__category;
|
|
||||||
return (char *)__msgid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *ngettext(const char *__msgid1, const char *__msgid2,
|
|
||||||
unsigned long int __n) {
|
|
||||||
return (char *)(__n == 1 ? __msgid1 : __msgid2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *dngettext(const char *__domainname, const char *__msgid1,
|
|
||||||
const char *__msgid2, unsigned long int __n) {
|
|
||||||
(void)__domainname;
|
|
||||||
return (char *)(__n == 1 ? __msgid1 : __msgid2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *dcngettext(const char *__domainname, const char *__msgid1,
|
|
||||||
const char *__msgid2, unsigned long int __n,
|
|
||||||
int __category) {
|
|
||||||
(void)__domainname;
|
|
||||||
(void)__category;
|
|
||||||
return (char *)(__n == 1 ? __msgid1 : __msgid2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *textdomain(const char *__domainname) {
|
|
||||||
return (char *)(__domainname ? __domainname : "messages");
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *bindtextdomain(const char *__domainname,
|
|
||||||
const char *__dirname) {
|
|
||||||
(void)__domainname;
|
|
||||||
return (char *)__dirname;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *bind_textdomain_codeset(const char *__domainname,
|
|
||||||
const char *__codeset) {
|
|
||||||
(void)__domainname;
|
|
||||||
return (char *)__codeset;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* libintl.h */
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* link.h — dynamic-linker introspection (dl_iterate_phdr).
|
|
||||||
*
|
|
||||||
* Non-POSIX (glibc/BSD extension). The implementation of dl_iterate_phdr lives
|
|
||||||
* in relibc's dynamic linker (src/header/link/mod.rs); this header only
|
|
||||||
* declares the ABI. Keep `struct dl_phdr_info` in sync with the Rust
|
|
||||||
* `DlPhdrInfo` struct in that module.
|
|
||||||
*/
|
|
||||||
#ifndef _RELIBC_LINK_H
|
|
||||||
#define _RELIBC_LINK_H
|
|
||||||
|
|
||||||
#include <elf.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* glibc-compatible `ElfW(type)` macro: expands to the native-word-size ELF type
|
|
||||||
* (Elf64_type on LP64, Elf32_type otherwise). Portable ELF code (Mesa's
|
|
||||||
* util/build_id.c, unwinders) uses `ElfW(Nhdr)` etc. and expects <link.h> to
|
|
||||||
* provide this.
|
|
||||||
*/
|
|
||||||
#ifndef ElfW
|
|
||||||
# if defined(__LP64__) || defined(_LP64) || (__SIZEOF_POINTER__ == 8)
|
|
||||||
# define ElfW(type) Elf64_##type
|
|
||||||
# else
|
|
||||||
# define ElfW(type) Elf32_##type
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct dl_phdr_info {
|
|
||||||
Elf64_Addr dlpi_addr; /* base / load bias of the object */
|
|
||||||
const char *dlpi_name; /* object pathname (NUL-terminated) */
|
|
||||||
const Elf64_Phdr *dlpi_phdr; /* pointer to the program header array */
|
|
||||||
Elf64_Half dlpi_phnum; /* number of program headers */
|
|
||||||
/* glibc-compatible extension fields */
|
|
||||||
unsigned long long dlpi_adds; /* # objects added since process start */
|
|
||||||
unsigned long long dlpi_subs; /* # objects removed since start */
|
|
||||||
size_t dlpi_tls_modid; /* TLS module ID (0 if none) */
|
|
||||||
void *dlpi_tls_data; /* TLS block for the calling thread */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Call `callback` once per loaded object. `size` is sizeof(struct dl_phdr_info).
|
|
||||||
* Iteration stops as soon as a callback returns non-zero, and that value is
|
|
||||||
* returned; otherwise the return value is that of the last callback (or 0 when
|
|
||||||
* there are no objects).
|
|
||||||
*/
|
|
||||||
int dl_iterate_phdr(int (*callback)(struct dl_phdr_info *info,
|
|
||||||
size_t size, void *data),
|
|
||||||
void *data);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _RELIBC_LINK_H */
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#ifndef __MACHINE_ENDIAN_H__
|
|
||||||
|
|
||||||
/* TODO: Forcing little endian, if you need a big endian system, fix this { */
|
|
||||||
#ifndef BIG_ENDIAN
|
|
||||||
#define BIG_ENDIAN 4321
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LITTLE_ENDIAN
|
|
||||||
#define LITTLE_ENDIAN 1234
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BYTE_ORDER
|
|
||||||
#define BYTE_ORDER LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
#endif /* __MACHINE_ENDIAN_H__ */
|
|
||||||
-113
@@ -1,113 +0,0 @@
|
|||||||
#include <openlibm_math.h>
|
|
||||||
|
|
||||||
// Missing typedefs
|
|
||||||
typedef float float_t;
|
|
||||||
typedef double double_t;
|
|
||||||
|
|
||||||
/* double */
|
|
||||||
|
|
||||||
#ifndef M_PI
|
|
||||||
#define M_PI 3.14159265358979323846 /* pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI_2
|
|
||||||
#define M_PI_2 1.57079632679489661923 /* pi/2 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI_4
|
|
||||||
#define M_PI_4 0.78539816339744830962 /* pi/4 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_2_PI
|
|
||||||
#define M_2_PI 0.63661977236758134308 /* 2/pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_E
|
|
||||||
#define M_E 2.7182818284590452354 /* e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LOG2E
|
|
||||||
#define M_LOG2E 1.4426950408889634074 /* log_2 e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LOG10E
|
|
||||||
#define M_LOG10E 0.43429448190325182765 /* log_10 e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LN2
|
|
||||||
#define M_LN2 0.69314718055994530942 /* log_e 2 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LN10
|
|
||||||
#define M_LN10 2.30258509299404568402 /* log_e 10 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_1_PI
|
|
||||||
#define M_1_PI 0.31830988618379067154 /* 1/pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_2_SQRTPI
|
|
||||||
#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_SQRT2
|
|
||||||
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_SQRT1_2
|
|
||||||
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* long double */
|
|
||||||
|
|
||||||
#ifndef M_El
|
|
||||||
#define M_El 2.718281828459045235360287471352662498L /* e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LOG2El
|
|
||||||
#define M_LOG2El 1.442695040888963407359924681001892137L /* log_2 e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LOG10El
|
|
||||||
#define M_LOG10El 0.434294481903251827651128918916605082L /* log_10 e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LN2l
|
|
||||||
#define M_LN2l 0.693147180559945309417232121458176568L /* log_e 2 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LN10l
|
|
||||||
#define M_LN10l 2.302585092994045684017991454684364208L /* log_e 10 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PIl
|
|
||||||
#define M_PIl 3.141592653589793238462643383279502884L /* pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI_2l
|
|
||||||
#define M_PI_2l 1.570796326794896619231321691639751442L /* pi/2 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI_4l
|
|
||||||
#define M_PI_4l 0.785398163397448309615660845819875721L /* pi/4 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_1_PIl
|
|
||||||
#define M_1_PIl 0.318309886183790671537767526745028724L /* 1/pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_2_PIl
|
|
||||||
#define M_2_PIl 0.636619772367581343075535053490057448L /* 2/pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_2_SQRTPIl
|
|
||||||
#define M_2_SQRTPIl 1.128379167095512573896158903121545172L /* 2/sqrt(pi) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_SQRT2l
|
|
||||||
#define M_SQRT2l 1.414213562373095048801688724209698079L /* sqrt(2) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_SQRT1_2l
|
|
||||||
#define M_SQRT1_2l 0.707106781186547524400844362104849039L /* 1/sqrt(2) */
|
|
||||||
#endif
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef _NETINET_IN_SYSTM_H
|
|
||||||
#define _NETINET_IN_SYSTM_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef uint16_t n_short;
|
|
||||||
typedef uint32_t n_long;
|
|
||||||
typedef uint32_t n_time;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef _RELIBC_PATHS_H
|
|
||||||
#define _RELIBC_PATHS_H
|
|
||||||
|
|
||||||
#define _PATH_BSHELL "/bin/sh"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/* search.h — POSIX search tables.
|
|
||||||
*
|
|
||||||
* Red Bear OS / relibc. Provides the binary-tree search family (tsearch,
|
|
||||||
* tfind, tdelete, twalk) plus the GNU tdestroy extension. Implemented in
|
|
||||||
* src/header/search/mod.rs; the VISIT enum order below MUST match the
|
|
||||||
* PREORDER/POSTORDER/ENDORDER/LEAF constants there.
|
|
||||||
*/
|
|
||||||
#ifndef _SEARCH_H
|
|
||||||
#define _SEARCH_H 1
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
preorder,
|
|
||||||
postorder,
|
|
||||||
endorder,
|
|
||||||
leaf
|
|
||||||
} VISIT;
|
|
||||||
|
|
||||||
void *tsearch(const void *__key, void **__rootp,
|
|
||||||
int (*__compar)(const void *, const void *));
|
|
||||||
void *tfind(const void *__key, void *const *__rootp,
|
|
||||||
int (*__compar)(const void *, const void *));
|
|
||||||
void *tdelete(const void *__key, void **__rootp,
|
|
||||||
int (*__compar)(const void *, const void *));
|
|
||||||
void twalk(const void *__root,
|
|
||||||
void (*__action)(const void *__nodep, VISIT __which, int __depth));
|
|
||||||
|
|
||||||
/* GNU extension. */
|
|
||||||
void tdestroy(void *__root, void (*__free_node)(void *__nodep));
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* search.h */
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#ifndef _SETJMP_H
|
|
||||||
#define _SETJMP_H
|
|
||||||
|
|
||||||
#ifdef __aarch64__
|
|
||||||
typedef unsigned long long jmp_buf[22];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __i386__
|
|
||||||
typedef unsigned long long jmp_buf[6];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __x86_64__
|
|
||||||
typedef unsigned long long jmp_buf[16];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __riscv
|
|
||||||
typedef unsigned long long jmp_buf[26];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef jmp_buf sigjmp_buf;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int setjmp(jmp_buf buf);
|
|
||||||
void longjmp(jmp_buf buf, int value);
|
|
||||||
int sigsetjmp(jmp_buf buf, int savemask);
|
|
||||||
int siglongjmp(jmp_buf buf, int savemask);
|
|
||||||
|
|
||||||
/* POSIX _setjmp/_longjmp. relibc already EXPORTS both -- `nm libc.a` shows
|
|
||||||
`T _setjmp` and `T _longjmp` as aliases of setjmp/longjmp -- they were
|
|
||||||
simply never declared here, so callers got an implicit declaration, which
|
|
||||||
GCC 14+ makes an error:
|
|
||||||
lua-5.4.4/src/ldo.c:66: error: implicit declaration of function
|
|
||||||
'_longjmp'; did you mean 'longjmp'?
|
|
||||||
(wireplumber bundles Lua, which uses them under LUA_USE_POSIX.)
|
|
||||||
The distinction from setjmp/longjmp is the signal mask, which relibc's
|
|
||||||
implementation does not save in either form, so the alias is accurate. */
|
|
||||||
int _setjmp(jmp_buf buf);
|
|
||||||
void _longjmp(jmp_buf buf, int value);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _SETJMP_H */
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
/* Copyright (C) 2013-2022 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This file is part of GCC.
|
|
||||||
|
|
||||||
GCC is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 3, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
GCC is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
Under Section 7 of GPL version 3, you are granted additional
|
|
||||||
permissions described in the GCC Runtime Library Exception, version
|
|
||||||
3.1, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License and
|
|
||||||
a copy of the GCC Runtime Library Exception along with this program;
|
|
||||||
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
/* ISO C11 Standard: 7.17 Atomics <stdatomic.h>. */
|
|
||||||
|
|
||||||
#ifndef _STDATOMIC_H
|
|
||||||
#define _STDATOMIC_H
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
memory_order_relaxed = __ATOMIC_RELAXED,
|
|
||||||
memory_order_consume = __ATOMIC_CONSUME,
|
|
||||||
memory_order_acquire = __ATOMIC_ACQUIRE,
|
|
||||||
memory_order_release = __ATOMIC_RELEASE,
|
|
||||||
memory_order_acq_rel = __ATOMIC_ACQ_REL,
|
|
||||||
memory_order_seq_cst = __ATOMIC_SEQ_CST
|
|
||||||
} memory_order;
|
|
||||||
|
|
||||||
|
|
||||||
typedef _Atomic _Bool atomic_bool;
|
|
||||||
typedef _Atomic char atomic_char;
|
|
||||||
typedef _Atomic signed char atomic_schar;
|
|
||||||
typedef _Atomic unsigned char atomic_uchar;
|
|
||||||
typedef _Atomic short atomic_short;
|
|
||||||
typedef _Atomic unsigned short atomic_ushort;
|
|
||||||
typedef _Atomic int atomic_int;
|
|
||||||
typedef _Atomic unsigned int atomic_uint;
|
|
||||||
typedef _Atomic long atomic_long;
|
|
||||||
typedef _Atomic unsigned long atomic_ulong;
|
|
||||||
typedef _Atomic long long atomic_llong;
|
|
||||||
typedef _Atomic unsigned long long atomic_ullong;
|
|
||||||
typedef _Atomic __CHAR16_TYPE__ atomic_char16_t;
|
|
||||||
typedef _Atomic __CHAR32_TYPE__ atomic_char32_t;
|
|
||||||
typedef _Atomic __WCHAR_TYPE__ atomic_wchar_t;
|
|
||||||
typedef _Atomic __INT_LEAST8_TYPE__ atomic_int_least8_t;
|
|
||||||
typedef _Atomic __UINT_LEAST8_TYPE__ atomic_uint_least8_t;
|
|
||||||
typedef _Atomic __INT_LEAST16_TYPE__ atomic_int_least16_t;
|
|
||||||
typedef _Atomic __UINT_LEAST16_TYPE__ atomic_uint_least16_t;
|
|
||||||
typedef _Atomic __INT_LEAST32_TYPE__ atomic_int_least32_t;
|
|
||||||
typedef _Atomic __UINT_LEAST32_TYPE__ atomic_uint_least32_t;
|
|
||||||
typedef _Atomic __INT_LEAST64_TYPE__ atomic_int_least64_t;
|
|
||||||
typedef _Atomic __UINT_LEAST64_TYPE__ atomic_uint_least64_t;
|
|
||||||
typedef _Atomic __INT_FAST8_TYPE__ atomic_int_fast8_t;
|
|
||||||
typedef _Atomic __UINT_FAST8_TYPE__ atomic_uint_fast8_t;
|
|
||||||
typedef _Atomic __INT_FAST16_TYPE__ atomic_int_fast16_t;
|
|
||||||
typedef _Atomic __UINT_FAST16_TYPE__ atomic_uint_fast16_t;
|
|
||||||
typedef _Atomic __INT_FAST32_TYPE__ atomic_int_fast32_t;
|
|
||||||
typedef _Atomic __UINT_FAST32_TYPE__ atomic_uint_fast32_t;
|
|
||||||
typedef _Atomic __INT_FAST64_TYPE__ atomic_int_fast64_t;
|
|
||||||
typedef _Atomic __UINT_FAST64_TYPE__ atomic_uint_fast64_t;
|
|
||||||
typedef _Atomic __INTPTR_TYPE__ atomic_intptr_t;
|
|
||||||
typedef _Atomic __UINTPTR_TYPE__ atomic_uintptr_t;
|
|
||||||
typedef _Atomic __SIZE_TYPE__ atomic_size_t;
|
|
||||||
typedef _Atomic __PTRDIFF_TYPE__ atomic_ptrdiff_t;
|
|
||||||
typedef _Atomic __INTMAX_TYPE__ atomic_intmax_t;
|
|
||||||
typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
|
|
||||||
|
|
||||||
|
|
||||||
#define ATOMIC_VAR_INIT(VALUE) (VALUE)
|
|
||||||
|
|
||||||
/* Initialize an atomic object pointed to by PTR with VAL. */
|
|
||||||
#define atomic_init(PTR, VAL) \
|
|
||||||
atomic_store_explicit (PTR, VAL, __ATOMIC_RELAXED)
|
|
||||||
|
|
||||||
#define kill_dependency(Y) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __kill_dependency_tmp = (Y); \
|
|
||||||
__kill_dependency_tmp; \
|
|
||||||
})
|
|
||||||
|
|
||||||
extern void atomic_thread_fence (memory_order);
|
|
||||||
#define atomic_thread_fence(MO) __atomic_thread_fence (MO)
|
|
||||||
extern void atomic_signal_fence (memory_order);
|
|
||||||
#define atomic_signal_fence(MO) __atomic_signal_fence (MO)
|
|
||||||
#define atomic_is_lock_free(OBJ) __atomic_is_lock_free (sizeof (*(OBJ)), (OBJ))
|
|
||||||
|
|
||||||
#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE
|
|
||||||
#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE
|
|
||||||
#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE
|
|
||||||
#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE
|
|
||||||
#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE
|
|
||||||
#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE
|
|
||||||
#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE
|
|
||||||
#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE
|
|
||||||
#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE
|
|
||||||
#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE
|
|
||||||
|
|
||||||
|
|
||||||
/* Note that these macros require __auto_type to remove
|
|
||||||
_Atomic qualifiers (and const qualifiers, if those are valid on
|
|
||||||
macro operands).
|
|
||||||
|
|
||||||
Also note that the header file uses the generic form of __atomic
|
|
||||||
builtins, which requires the address to be taken of the value
|
|
||||||
parameter, and then we pass that value on. This allows the macros
|
|
||||||
to work for any type, and the compiler is smart enough to convert
|
|
||||||
these to lock-free _N variants if possible, and throw away the
|
|
||||||
temps. */
|
|
||||||
|
|
||||||
#define atomic_store_explicit(PTR, VAL, MO) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_store_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_store_ptr) __atomic_store_tmp = (VAL); \
|
|
||||||
__atomic_store (__atomic_store_ptr, &__atomic_store_tmp, (MO)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_store(PTR, VAL) \
|
|
||||||
atomic_store_explicit (PTR, VAL, __ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
|
|
||||||
#define atomic_load_explicit(PTR, MO) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_load_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_load_ptr) __atomic_load_tmp; \
|
|
||||||
__atomic_load (__atomic_load_ptr, &__atomic_load_tmp, (MO)); \
|
|
||||||
__atomic_load_tmp; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_load(PTR) atomic_load_explicit (PTR, __ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
|
|
||||||
#define atomic_exchange_explicit(PTR, VAL, MO) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_exchange_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_exchange_ptr) __atomic_exchange_val = (VAL); \
|
|
||||||
__typeof__ ((void)0, *__atomic_exchange_ptr) __atomic_exchange_tmp; \
|
|
||||||
__atomic_exchange (__atomic_exchange_ptr, &__atomic_exchange_val, \
|
|
||||||
&__atomic_exchange_tmp, (MO)); \
|
|
||||||
__atomic_exchange_tmp; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_exchange(PTR, VAL) \
|
|
||||||
atomic_exchange_explicit (PTR, VAL, __ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
|
|
||||||
#define atomic_compare_exchange_strong_explicit(PTR, VAL, DES, SUC, FAIL) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_compare_exchange_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
|
|
||||||
= (DES); \
|
|
||||||
__atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL), \
|
|
||||||
&__atomic_compare_exchange_tmp, 0, \
|
|
||||||
(SUC), (FAIL)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_compare_exchange_strong(PTR, VAL, DES) \
|
|
||||||
atomic_compare_exchange_strong_explicit (PTR, VAL, DES, __ATOMIC_SEQ_CST, \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
#define atomic_compare_exchange_weak_explicit(PTR, VAL, DES, SUC, FAIL) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_compare_exchange_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
|
|
||||||
= (DES); \
|
|
||||||
__atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL), \
|
|
||||||
&__atomic_compare_exchange_tmp, 1, \
|
|
||||||
(SUC), (FAIL)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_compare_exchange_weak(PTR, VAL, DES) \
|
|
||||||
atomic_compare_exchange_weak_explicit (PTR, VAL, DES, __ATOMIC_SEQ_CST, \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define atomic_fetch_add(PTR, VAL) __atomic_fetch_add ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_add_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_add ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
#define atomic_fetch_sub(PTR, VAL) __atomic_fetch_sub ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_sub_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_sub ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
#define atomic_fetch_or(PTR, VAL) __atomic_fetch_or ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_or_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_or ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
#define atomic_fetch_xor(PTR, VAL) __atomic_fetch_xor ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_xor_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_xor ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
#define atomic_fetch_and(PTR, VAL) __atomic_fetch_and ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_and_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_and ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
|
|
||||||
typedef _Atomic struct
|
|
||||||
{
|
|
||||||
#if __GCC_ATOMIC_TEST_AND_SET_TRUEVAL == 1
|
|
||||||
_Bool __val;
|
|
||||||
#else
|
|
||||||
unsigned char __val;
|
|
||||||
#endif
|
|
||||||
} atomic_flag;
|
|
||||||
|
|
||||||
#define ATOMIC_FLAG_INIT { 0 }
|
|
||||||
|
|
||||||
|
|
||||||
extern _Bool atomic_flag_test_and_set (volatile atomic_flag *);
|
|
||||||
#define atomic_flag_test_and_set(PTR) \
|
|
||||||
__atomic_test_and_set ((PTR), __ATOMIC_SEQ_CST)
|
|
||||||
extern _Bool atomic_flag_test_and_set_explicit (volatile atomic_flag *,
|
|
||||||
memory_order);
|
|
||||||
#define atomic_flag_test_and_set_explicit(PTR, MO) \
|
|
||||||
__atomic_test_and_set ((PTR), (MO))
|
|
||||||
|
|
||||||
extern void atomic_flag_clear (volatile atomic_flag *);
|
|
||||||
#define atomic_flag_clear(PTR) __atomic_clear ((PTR), __ATOMIC_SEQ_CST)
|
|
||||||
extern void atomic_flag_clear_explicit (volatile atomic_flag *, memory_order);
|
|
||||||
#define atomic_flag_clear_explicit(PTR, MO) __atomic_clear ((PTR), (MO))
|
|
||||||
|
|
||||||
#endif /* _STDATOMIC_H */
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#ifndef _STDBOOL_H
|
|
||||||
#define _STDBOOL_H
|
|
||||||
|
|
||||||
#ifndef __cplusplus
|
|
||||||
#define bool _Bool
|
|
||||||
#define true 1
|
|
||||||
#define false 0
|
|
||||||
#else /* __cplusplus */
|
|
||||||
#if __cplusplus < 201103L
|
|
||||||
#define bool bool
|
|
||||||
#define false false
|
|
||||||
#define true true
|
|
||||||
#endif /*__cplusplus < 201103L*/
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
#define __bool_true_false_are_defined 1
|
|
||||||
|
|
||||||
#endif /* _STDBOOL_H */
|
|
||||||
@@ -1,370 +0,0 @@
|
|||||||
/* Copyright (C) 2008-2018 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This file is part of GCC.
|
|
||||||
|
|
||||||
GCC is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 3, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
GCC is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
Under Section 7 of GPL version 3, you are granted additional
|
|
||||||
permissions described in the GCC Runtime Library Exception, version
|
|
||||||
3.1, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License and
|
|
||||||
a copy of the GCC Runtime Library Exception along with this program;
|
|
||||||
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ISO C Standard: 7.18 Integer types <stdint.h>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _STDINT_H
|
|
||||||
#define _STDINT_H
|
|
||||||
|
|
||||||
/* 7.8.1.1 Exact-width integer types */
|
|
||||||
|
|
||||||
#ifdef __INT8_TYPE__
|
|
||||||
typedef __INT8_TYPE__ int8_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __INT16_TYPE__
|
|
||||||
typedef __INT16_TYPE__ int16_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __INT32_TYPE__
|
|
||||||
typedef __INT32_TYPE__ int32_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __INT64_TYPE__
|
|
||||||
typedef __INT64_TYPE__ int64_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT8_TYPE__
|
|
||||||
typedef __UINT8_TYPE__ uint8_t;
|
|
||||||
typedef __UINT8_TYPE__ u_int8_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT16_TYPE__
|
|
||||||
typedef __UINT16_TYPE__ uint16_t;
|
|
||||||
typedef __UINT16_TYPE__ u_int16_t;
|
|
||||||
#endif
|
|
||||||
#include <bits/uint32-t.h> // for uint32_t
|
|
||||||
#ifdef __UINT64_TYPE__
|
|
||||||
typedef __UINT64_TYPE__ uint64_t;
|
|
||||||
typedef __UINT64_TYPE__ u_int64_t;
|
|
||||||
// Required by openlibm
|
|
||||||
typedef __UINT64_TYPE__ __uint64_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 7.8.1.2 Minimum-width integer types */
|
|
||||||
|
|
||||||
typedef __INT_LEAST8_TYPE__ int_least8_t;
|
|
||||||
typedef __INT_LEAST16_TYPE__ int_least16_t;
|
|
||||||
typedef __INT_LEAST32_TYPE__ int_least32_t;
|
|
||||||
typedef __INT_LEAST64_TYPE__ int_least64_t;
|
|
||||||
typedef __UINT_LEAST8_TYPE__ uint_least8_t;
|
|
||||||
typedef __UINT_LEAST16_TYPE__ uint_least16_t;
|
|
||||||
typedef __UINT_LEAST32_TYPE__ uint_least32_t;
|
|
||||||
typedef __UINT_LEAST64_TYPE__ uint_least64_t;
|
|
||||||
|
|
||||||
/* 7.8.1.3 Fastest minimum-width integer types */
|
|
||||||
|
|
||||||
typedef __INT_FAST8_TYPE__ int_fast8_t;
|
|
||||||
typedef __INT_FAST16_TYPE__ int_fast16_t;
|
|
||||||
typedef __INT_FAST32_TYPE__ int_fast32_t;
|
|
||||||
typedef __INT_FAST64_TYPE__ int_fast64_t;
|
|
||||||
typedef __UINT_FAST8_TYPE__ uint_fast8_t;
|
|
||||||
typedef __UINT_FAST16_TYPE__ uint_fast16_t;
|
|
||||||
typedef __UINT_FAST32_TYPE__ uint_fast32_t;
|
|
||||||
typedef __UINT_FAST64_TYPE__ uint_fast64_t;
|
|
||||||
|
|
||||||
/* 7.8.1.4 Integer types capable of holding object pointers */
|
|
||||||
|
|
||||||
#include <bits/intptr-t.h> // for intptr_t
|
|
||||||
#ifdef __UINTPTR_TYPE__
|
|
||||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 7.8.1.5 Greatest-width integer types */
|
|
||||||
|
|
||||||
typedef __INTMAX_TYPE__ intmax_t;
|
|
||||||
typedef __UINTMAX_TYPE__ uintmax_t;
|
|
||||||
|
|
||||||
#if (!defined __cplusplus || __cplusplus >= 201103L \
|
|
||||||
|| defined __STDC_LIMIT_MACROS)
|
|
||||||
|
|
||||||
/* 7.18.2 Limits of specified-width integer types */
|
|
||||||
|
|
||||||
#ifdef __INT8_MAX__
|
|
||||||
# undef INT8_MAX
|
|
||||||
# define INT8_MAX __INT8_MAX__
|
|
||||||
# undef INT8_MIN
|
|
||||||
# define INT8_MIN (-INT8_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT8_MAX__
|
|
||||||
# undef UINT8_MAX
|
|
||||||
# define UINT8_MAX __UINT8_MAX__
|
|
||||||
#endif
|
|
||||||
#ifdef __INT16_MAX__
|
|
||||||
# undef INT16_MAX
|
|
||||||
# define INT16_MAX __INT16_MAX__
|
|
||||||
# undef INT16_MIN
|
|
||||||
# define INT16_MIN (-INT16_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT16_MAX__
|
|
||||||
# undef UINT16_MAX
|
|
||||||
# define UINT16_MAX __UINT16_MAX__
|
|
||||||
#endif
|
|
||||||
#ifdef __INT32_MAX__
|
|
||||||
# undef INT32_MAX
|
|
||||||
# define INT32_MAX __INT32_MAX__
|
|
||||||
# undef INT32_MIN
|
|
||||||
# define INT32_MIN (-INT32_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT32_MAX__
|
|
||||||
# undef UINT32_MAX
|
|
||||||
# define UINT32_MAX __UINT32_MAX__
|
|
||||||
#endif
|
|
||||||
#ifdef __INT64_MAX__
|
|
||||||
# undef INT64_MAX
|
|
||||||
# define INT64_MAX __INT64_MAX__
|
|
||||||
# undef INT64_MIN
|
|
||||||
# define INT64_MIN (-INT64_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT64_MAX__
|
|
||||||
# undef UINT64_MAX
|
|
||||||
# define UINT64_MAX __UINT64_MAX__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef INT_LEAST8_MAX
|
|
||||||
#define INT_LEAST8_MAX __INT_LEAST8_MAX__
|
|
||||||
#undef INT_LEAST8_MIN
|
|
||||||
#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1)
|
|
||||||
#undef UINT_LEAST8_MAX
|
|
||||||
#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__
|
|
||||||
#undef INT_LEAST16_MAX
|
|
||||||
#define INT_LEAST16_MAX __INT_LEAST16_MAX__
|
|
||||||
#undef INT_LEAST16_MIN
|
|
||||||
#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1)
|
|
||||||
#undef UINT_LEAST16_MAX
|
|
||||||
#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__
|
|
||||||
#undef INT_LEAST32_MAX
|
|
||||||
#define INT_LEAST32_MAX __INT_LEAST32_MAX__
|
|
||||||
#undef INT_LEAST32_MIN
|
|
||||||
#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1)
|
|
||||||
#undef UINT_LEAST32_MAX
|
|
||||||
#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__
|
|
||||||
#undef INT_LEAST64_MAX
|
|
||||||
#define INT_LEAST64_MAX __INT_LEAST64_MAX__
|
|
||||||
#undef INT_LEAST64_MIN
|
|
||||||
#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1)
|
|
||||||
#undef UINT_LEAST64_MAX
|
|
||||||
#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__
|
|
||||||
|
|
||||||
#undef INT_FAST8_MAX
|
|
||||||
#define INT_FAST8_MAX __INT_FAST8_MAX__
|
|
||||||
#undef INT_FAST8_MIN
|
|
||||||
#define INT_FAST8_MIN (-INT_FAST8_MAX - 1)
|
|
||||||
#undef UINT_FAST8_MAX
|
|
||||||
#define UINT_FAST8_MAX __UINT_FAST8_MAX__
|
|
||||||
#undef INT_FAST16_MAX
|
|
||||||
#define INT_FAST16_MAX __INT_FAST16_MAX__
|
|
||||||
#undef INT_FAST16_MIN
|
|
||||||
#define INT_FAST16_MIN (-INT_FAST16_MAX - 1)
|
|
||||||
#undef UINT_FAST16_MAX
|
|
||||||
#define UINT_FAST16_MAX __UINT_FAST16_MAX__
|
|
||||||
#undef INT_FAST32_MAX
|
|
||||||
#define INT_FAST32_MAX __INT_FAST32_MAX__
|
|
||||||
#undef INT_FAST32_MIN
|
|
||||||
#define INT_FAST32_MIN (-INT_FAST32_MAX - 1)
|
|
||||||
#undef UINT_FAST32_MAX
|
|
||||||
#define UINT_FAST32_MAX __UINT_FAST32_MAX__
|
|
||||||
#undef INT_FAST64_MAX
|
|
||||||
#define INT_FAST64_MAX __INT_FAST64_MAX__
|
|
||||||
#undef INT_FAST64_MIN
|
|
||||||
#define INT_FAST64_MIN (-INT_FAST64_MAX - 1)
|
|
||||||
#undef UINT_FAST64_MAX
|
|
||||||
#define UINT_FAST64_MAX __UINT_FAST64_MAX__
|
|
||||||
|
|
||||||
#ifdef __INTPTR_MAX__
|
|
||||||
# undef INTPTR_MAX
|
|
||||||
# define INTPTR_MAX __INTPTR_MAX__
|
|
||||||
# undef INTPTR_MIN
|
|
||||||
# define INTPTR_MIN (-INTPTR_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINTPTR_MAX__
|
|
||||||
# undef UINTPTR_MAX
|
|
||||||
# define UINTPTR_MAX __UINTPTR_MAX__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef INTMAX_MAX
|
|
||||||
#define INTMAX_MAX __INTMAX_MAX__
|
|
||||||
#undef INTMAX_MIN
|
|
||||||
#define INTMAX_MIN (-INTMAX_MAX - 1)
|
|
||||||
#undef UINTMAX_MAX
|
|
||||||
#define UINTMAX_MAX __UINTMAX_MAX__
|
|
||||||
|
|
||||||
/* 7.18.3 Limits of other integer types */
|
|
||||||
|
|
||||||
#undef PTRDIFF_MAX
|
|
||||||
#define PTRDIFF_MAX __PTRDIFF_MAX__
|
|
||||||
#undef PTRDIFF_MIN
|
|
||||||
#define PTRDIFF_MIN (-PTRDIFF_MAX - 1)
|
|
||||||
|
|
||||||
#undef SIG_ATOMIC_MAX
|
|
||||||
#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
|
|
||||||
#undef SIG_ATOMIC_MIN
|
|
||||||
#define SIG_ATOMIC_MIN __SIG_ATOMIC_MIN__
|
|
||||||
|
|
||||||
#undef SIZE_MAX
|
|
||||||
#define SIZE_MAX __SIZE_MAX__
|
|
||||||
|
|
||||||
#undef WCHAR_MAX
|
|
||||||
#define WCHAR_MAX __WCHAR_MAX__
|
|
||||||
#undef WCHAR_MIN
|
|
||||||
#define WCHAR_MIN __WCHAR_MIN__
|
|
||||||
|
|
||||||
#undef WINT_MAX
|
|
||||||
#define WINT_MAX __WINT_MAX__
|
|
||||||
#undef WINT_MIN
|
|
||||||
#define WINT_MIN __WINT_MIN__
|
|
||||||
|
|
||||||
#endif /* (!defined __cplusplus || __cplusplus >= 201103L
|
|
||||||
|| defined __STDC_LIMIT_MACROS) */
|
|
||||||
|
|
||||||
#if (!defined __cplusplus || __cplusplus >= 201103L \
|
|
||||||
|| defined __STDC_CONSTANT_MACROS)
|
|
||||||
|
|
||||||
#undef INT8_C
|
|
||||||
#define INT8_C(c) __INT8_C(c)
|
|
||||||
#undef INT16_C
|
|
||||||
#define INT16_C(c) __INT16_C(c)
|
|
||||||
#undef INT32_C
|
|
||||||
#define INT32_C(c) __INT32_C(c)
|
|
||||||
#undef INT64_C
|
|
||||||
#define INT64_C(c) __INT64_C(c)
|
|
||||||
#undef UINT8_C
|
|
||||||
#define UINT8_C(c) __UINT8_C(c)
|
|
||||||
#undef UINT16_C
|
|
||||||
#define UINT16_C(c) __UINT16_C(c)
|
|
||||||
#undef UINT32_C
|
|
||||||
#define UINT32_C(c) __UINT32_C(c)
|
|
||||||
#undef UINT64_C
|
|
||||||
#define UINT64_C(c) __UINT64_C(c)
|
|
||||||
#undef INTMAX_C
|
|
||||||
#define INTMAX_C(c) __INTMAX_C(c)
|
|
||||||
#undef UINTMAX_C
|
|
||||||
#define UINTMAX_C(c) __UINTMAX_C(c)
|
|
||||||
|
|
||||||
#endif /* (!defined __cplusplus || __cplusplus >= 201103L
|
|
||||||
|| defined __STDC_CONSTANT_MACROS) */
|
|
||||||
|
|
||||||
#ifdef __STDC_WANT_IEC_60559_BFP_EXT__
|
|
||||||
/* TS 18661-1 widths of integer types. */
|
|
||||||
|
|
||||||
#ifdef __INT8_TYPE__
|
|
||||||
# undef INT8_WIDTH
|
|
||||||
# define INT8_WIDTH 8
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT8_TYPE__
|
|
||||||
# undef UINT8_WIDTH
|
|
||||||
# define UINT8_WIDTH 8
|
|
||||||
#endif
|
|
||||||
#ifdef __INT16_TYPE__
|
|
||||||
# undef INT16_WIDTH
|
|
||||||
# define INT16_WIDTH 16
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT16_TYPE__
|
|
||||||
# undef UINT16_WIDTH
|
|
||||||
# define UINT16_WIDTH 16
|
|
||||||
#endif
|
|
||||||
#ifdef __INT32_TYPE__
|
|
||||||
# undef INT32_WIDTH
|
|
||||||
# define INT32_WIDTH 32
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT32_TYPE__
|
|
||||||
# undef UINT32_WIDTH
|
|
||||||
# define UINT32_WIDTH 32
|
|
||||||
#endif
|
|
||||||
#ifdef __INT64_TYPE__
|
|
||||||
# undef INT64_WIDTH
|
|
||||||
# define INT64_WIDTH 64
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT64_TYPE__
|
|
||||||
# undef UINT64_WIDTH
|
|
||||||
# define UINT64_WIDTH 64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef INT_LEAST8_WIDTH
|
|
||||||
#define INT_LEAST8_WIDTH __INT_LEAST8_WIDTH__
|
|
||||||
#undef UINT_LEAST8_WIDTH
|
|
||||||
#define UINT_LEAST8_WIDTH __INT_LEAST8_WIDTH__
|
|
||||||
#undef INT_LEAST16_WIDTH
|
|
||||||
#define INT_LEAST16_WIDTH __INT_LEAST16_WIDTH__
|
|
||||||
#undef UINT_LEAST16_WIDTH
|
|
||||||
#define UINT_LEAST16_WIDTH __INT_LEAST16_WIDTH__
|
|
||||||
#undef INT_LEAST32_WIDTH
|
|
||||||
#define INT_LEAST32_WIDTH __INT_LEAST32_WIDTH__
|
|
||||||
#undef UINT_LEAST32_WIDTH
|
|
||||||
#define UINT_LEAST32_WIDTH __INT_LEAST32_WIDTH__
|
|
||||||
#undef INT_LEAST64_WIDTH
|
|
||||||
#define INT_LEAST64_WIDTH __INT_LEAST64_WIDTH__
|
|
||||||
#undef UINT_LEAST64_WIDTH
|
|
||||||
#define UINT_LEAST64_WIDTH __INT_LEAST64_WIDTH__
|
|
||||||
|
|
||||||
#undef INT_FAST8_WIDTH
|
|
||||||
#define INT_FAST8_WIDTH __INT_FAST8_WIDTH__
|
|
||||||
#undef UINT_FAST8_WIDTH
|
|
||||||
#define UINT_FAST8_WIDTH __INT_FAST8_WIDTH__
|
|
||||||
#undef INT_FAST16_WIDTH
|
|
||||||
#define INT_FAST16_WIDTH __INT_FAST16_WIDTH__
|
|
||||||
#undef UINT_FAST16_WIDTH
|
|
||||||
#define UINT_FAST16_WIDTH __INT_FAST16_WIDTH__
|
|
||||||
#undef INT_FAST32_WIDTH
|
|
||||||
#define INT_FAST32_WIDTH __INT_FAST32_WIDTH__
|
|
||||||
#undef UINT_FAST32_WIDTH
|
|
||||||
#define UINT_FAST32_WIDTH __INT_FAST32_WIDTH__
|
|
||||||
#undef INT_FAST64_WIDTH
|
|
||||||
#define INT_FAST64_WIDTH __INT_FAST64_WIDTH__
|
|
||||||
#undef UINT_FAST64_WIDTH
|
|
||||||
#define UINT_FAST64_WIDTH __INT_FAST64_WIDTH__
|
|
||||||
|
|
||||||
#ifdef __INTPTR_TYPE__
|
|
||||||
# undef INTPTR_WIDTH
|
|
||||||
# define INTPTR_WIDTH __INTPTR_WIDTH__
|
|
||||||
#endif
|
|
||||||
#ifdef __UINTPTR_TYPE__
|
|
||||||
# undef UINTPTR_WIDTH
|
|
||||||
# define UINTPTR_WIDTH __INTPTR_WIDTH__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef INTMAX_WIDTH
|
|
||||||
#define INTMAX_WIDTH __INTMAX_WIDTH__
|
|
||||||
#undef UINTMAX_WIDTH
|
|
||||||
#define UINTMAX_WIDTH __INTMAX_WIDTH__
|
|
||||||
|
|
||||||
#undef PTRDIFF_WIDTH
|
|
||||||
#define PTRDIFF_WIDTH __PTRDIFF_WIDTH__
|
|
||||||
|
|
||||||
#undef SIG_ATOMIC_WIDTH
|
|
||||||
#define SIG_ATOMIC_WIDTH __SIG_ATOMIC_WIDTH__
|
|
||||||
|
|
||||||
#undef SIZE_WIDTH
|
|
||||||
#define SIZE_WIDTH __SIZE_WIDTH__
|
|
||||||
|
|
||||||
#undef WCHAR_WIDTH
|
|
||||||
#define WCHAR_WIDTH __WCHAR_WIDTH__
|
|
||||||
|
|
||||||
#undef WINT_WIDTH
|
|
||||||
#define WINT_WIDTH __WINT_WIDTH__
|
|
||||||
|
|
||||||
#ifdef __ILP32__
|
|
||||||
#define SIZE_MAX UINT32_MAX
|
|
||||||
#else
|
|
||||||
#define SIZE_MAX UINT64_MAX
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _GCC_STDINT_H */
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#ifndef _STDIO_EXT_H
|
|
||||||
#define _STDIO_EXT_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t __freadahead(FILE *stream);
|
|
||||||
size_t __fpending(FILE *stream);
|
|
||||||
int __freadable(FILE *stream);
|
|
||||||
int __freading(FILE *stream);
|
|
||||||
void __fseterr(FILE *stream);
|
|
||||||
int __fwritable(FILE *stream);
|
|
||||||
int __fwriting(FILE *stream);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _STDIO_EXT_H */
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/* Spec:
|
|
||||||
* The <stdnoreturn.h> header shall define the macro noreturn which shall
|
|
||||||
* expand to _Noreturn */
|
|
||||||
|
|
||||||
#ifndef _STDNORETURN_H
|
|
||||||
#define _STDNORETURN_H
|
|
||||||
|
|
||||||
#ifndef __cplusplus
|
|
||||||
/* Borrowed from musl */
|
|
||||||
#if __STDC_VERSION__ >= 201112L
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#define _Noreturn __attribute__((__noreturn__))
|
|
||||||
#else
|
|
||||||
#define _Noreturn
|
|
||||||
#endif
|
|
||||||
#define noreturn _Noreturn
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
#ifndef _SYS_IOCCOM_H
|
|
||||||
#define _SYS_IOCCOM_H
|
|
||||||
|
|
||||||
// Linux-compatible ioctl command encoding constants — supplements the
|
|
||||||
// base _IOC / _IO / _IOR / _IOW / _IOWR macros from <sys/ioctl.h> with
|
|
||||||
// the bitfield widths, masks, shifts, decoders, and IOC direction
|
|
||||||
// macros that Linux UAPI headers (DRM, V4L2, etc.) expect.
|
|
||||||
//
|
|
||||||
// DRM headers include <sys/ioccom.h> as the BSD-naming path for ioctl
|
|
||||||
// command encoding; relibc's <sys/ioctl.h> provides the basic macros
|
|
||||||
// but not these constants. This header unifies them so DRM and other
|
|
||||||
// Linux UAPI consumers build without a Mesa-side stub.
|
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
|
|
||||||
#ifndef _IOC_NRBITS
|
|
||||||
#define _IOC_NRBITS 8
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_TYPEBITS
|
|
||||||
#define _IOC_TYPEBITS 8
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_SIZEBITS
|
|
||||||
#define _IOC_SIZEBITS 14
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_DIRBITS
|
|
||||||
#define _IOC_DIRBITS 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _IOC_NRMASK
|
|
||||||
#define _IOC_NRMASK ((1 << _IOC_NRBITS) - 1)
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_TYPEMASK
|
|
||||||
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS) - 1)
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_SIZEMASK
|
|
||||||
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS) - 1)
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_DIRMASK
|
|
||||||
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS) - 1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _IOC_NRSHIFT
|
|
||||||
#define _IOC_NRSHIFT 0
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_TYPESHIFT
|
|
||||||
#define _IOC_TYPESHIFT (_IOC_NRSHIFT + _IOC_NRBITS)
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_SIZESHIFT
|
|
||||||
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT + _IOC_TYPEBITS)
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_DIRSHIFT
|
|
||||||
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT + _IOC_SIZEBITS)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// _IOC_TYPECHECK wraps the ioctl size argument in a sizeof() for type
|
|
||||||
// safety. The musl/relibc _IOW / _IOR / _IOWR macros already call
|
|
||||||
// sizeof internally on the third argument; this macro is provided for
|
|
||||||
// callers that compose ioctl numbers directly (e.g. DRM).
|
|
||||||
#ifndef _IOC_TYPECHECK
|
|
||||||
#define _IOC_TYPECHECK(t) (sizeof(t))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// _IOC_DIR / _IOC_TYPE / _IOC_NR / _IOC_SIZE decode the fields of an
|
|
||||||
// ioctl number. Linux UAPI callers use these to inspect ioctls.
|
|
||||||
#ifndef _IOC_DIR
|
|
||||||
#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_TYPE
|
|
||||||
#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_NR
|
|
||||||
#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
|
|
||||||
#endif
|
|
||||||
#ifndef _IOC_SIZE
|
|
||||||
#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// IOC_IN / IOC_OUT / IOC_INOUT are the values stored in the direction
|
|
||||||
// field of an ioctl number when constructing one without _IO / _IOR /
|
|
||||||
// _IOW / _IOWR.
|
|
||||||
#ifndef IOC_IN
|
|
||||||
#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
|
|
||||||
#endif
|
|
||||||
#ifndef IOC_OUT
|
|
||||||
#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
|
|
||||||
#endif
|
|
||||||
#ifndef IOC_INOUT
|
|
||||||
#define IOC_INOUT ((_IOC_WRITE | _IOC_READ) << _IOC_DIRSHIFT)
|
|
||||||
#endif
|
|
||||||
#ifndef IOCSIZE_MASK
|
|
||||||
#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
|
|
||||||
#endif
|
|
||||||
#ifndef IOCSIZE_SHIFT
|
|
||||||
#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _SYS_IOCCOM_H */
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#ifndef _SYS_PARAM_H
|
|
||||||
#define _SYS_PARAM_H
|
|
||||||
|
|
||||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
|
||||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
|
||||||
|
|
||||||
#define __bitop(array, index, op) ((array)[(index) / 8] op (1 << (index) % 8))
|
|
||||||
#define setbit(array, index) __bitop(array, index, |=)
|
|
||||||
#define clrbit(array, index) __bitop(array, index, &= ~)
|
|
||||||
#define isset(array, index) __bitop(array, index, &)
|
|
||||||
#define isclr(array, index) !isset(array, index)
|
|
||||||
|
|
||||||
#define howmany(bits, size) (((bits) + (size) - 1) / (size))
|
|
||||||
#define roundup(bits, size) (howmany(bits, size) * (size))
|
|
||||||
#define powerof2(n) !(((n) - 1) & (n))
|
|
||||||
|
|
||||||
// Shamelessly copied from musl.
|
|
||||||
// Tweak as needed.
|
|
||||||
#define MAXSYMLINKS 20
|
|
||||||
#define MAXHOSTNAMELEN 64
|
|
||||||
#define MAXNAMLEN 255
|
|
||||||
#define MAXPATHLEN 4096
|
|
||||||
#define NBBY 8
|
|
||||||
#define NGROUPS 32
|
|
||||||
#define CANBSIZ 255
|
|
||||||
#define NOFILE 256
|
|
||||||
#define NCARGS 131072
|
|
||||||
#define DEV_BSIZE 512
|
|
||||||
#define NOGROUP (-1)
|
|
||||||
|
|
||||||
#include <sys/resource.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include <machine/endian.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <poll.h>
|
|
||||||
@@ -1,846 +0,0 @@
|
|||||||
/* $NetBSD: queue.h,v 1.70 2015/11/02 15:21:23 christos Exp $ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 1991, 1993
|
|
||||||
* The Regents of the University of California. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the University nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SYS_QUEUE_H_
|
|
||||||
#define _SYS_QUEUE_H_
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file defines five types of data structures: singly-linked lists,
|
|
||||||
* lists, simple queues, tail queues, and circular queues.
|
|
||||||
*
|
|
||||||
* A singly-linked list is headed by a single forward pointer. The
|
|
||||||
* elements are singly linked for minimum space and pointer manipulation
|
|
||||||
* overhead at the expense of O(n) removal for arbitrary elements. New
|
|
||||||
* elements can be added to the list after an existing element or at the
|
|
||||||
* head of the list. Elements being removed from the head of the list
|
|
||||||
* should use the explicit macro for this purpose for optimum
|
|
||||||
* efficiency. A singly-linked list may only be traversed in the forward
|
|
||||||
* direction. Singly-linked lists are ideal for applications with large
|
|
||||||
* datasets and few or no removals or for implementing a LIFO queue.
|
|
||||||
*
|
|
||||||
* A list is headed by a single forward pointer (or an array of forward
|
|
||||||
* pointers for a hash table header). The elements are doubly linked
|
|
||||||
* so that an arbitrary element can be removed without a need to
|
|
||||||
* traverse the list. New elements can be added to the list before
|
|
||||||
* or after an existing element or at the head of the list. A list
|
|
||||||
* may only be traversed in the forward direction.
|
|
||||||
*
|
|
||||||
* A simple queue is headed by a pair of pointers, one the head of the
|
|
||||||
* list and the other to the tail of the list. The elements are singly
|
|
||||||
* linked to save space, so elements can only be removed from the
|
|
||||||
* head of the list. New elements can be added to the list after
|
|
||||||
* an existing element, at the head of the list, or at the end of the
|
|
||||||
* list. A simple queue may only be traversed in the forward direction.
|
|
||||||
*
|
|
||||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
|
||||||
* list and the other to the tail of the list. The elements are doubly
|
|
||||||
* linked so that an arbitrary element can be removed without a need to
|
|
||||||
* traverse the list. New elements can be added to the list before or
|
|
||||||
* after an existing element, at the head of the list, or at the end of
|
|
||||||
* the list. A tail queue may be traversed in either direction.
|
|
||||||
*
|
|
||||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
|
||||||
* list and the other to the tail of the list. The elements are doubly
|
|
||||||
* linked so that an arbitrary element can be removed without a need to
|
|
||||||
* traverse the list. New elements can be added to the list before or after
|
|
||||||
* an existing element, at the head of the list, or at the end of the list.
|
|
||||||
* A circle queue may be traversed in either direction, but has a more
|
|
||||||
* complex end of list detection.
|
|
||||||
*
|
|
||||||
* For details on the use of these macros, see the queue(3) manual page.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Include the definition of NULL only on NetBSD because sys/null.h
|
|
||||||
* is not available elsewhere. This conditional makes the header
|
|
||||||
* portable and it can simply be dropped verbatim into any system.
|
|
||||||
* The caveat is that on other systems some other header
|
|
||||||
* must provide NULL before the macros can be used.
|
|
||||||
*/
|
|
||||||
#ifdef __NetBSD__
|
|
||||||
#include <sys/null.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(QUEUEDEBUG)
|
|
||||||
# if defined(_KERNEL)
|
|
||||||
# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__)
|
|
||||||
# else
|
|
||||||
# include <err.h>
|
|
||||||
# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List definitions.
|
|
||||||
*/
|
|
||||||
#define SLIST_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *slh_first; /* first element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SLIST_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define SLIST_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *sle_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List access methods.
|
|
||||||
*/
|
|
||||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
|
||||||
#define SLIST_END(head) NULL
|
|
||||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
|
||||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
|
||||||
|
|
||||||
#define SLIST_FOREACH(var, head, field) \
|
|
||||||
for((var) = (head)->slh_first; \
|
|
||||||
(var) != SLIST_END(head); \
|
|
||||||
(var) = (var)->field.sle_next)
|
|
||||||
|
|
||||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = SLIST_FIRST((head)); \
|
|
||||||
(var) != SLIST_END(head) && \
|
|
||||||
((tvar) = SLIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List functions.
|
|
||||||
*/
|
|
||||||
#define SLIST_INIT(head) do { \
|
|
||||||
(head)->slh_first = SLIST_END(head); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
|
||||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
|
||||||
(slistelm)->field.sle_next = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
(elm)->field.sle_next = (head)->slh_first; \
|
|
||||||
(head)->slh_first = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE_AFTER(slistelm, field) do { \
|
|
||||||
(slistelm)->field.sle_next = \
|
|
||||||
SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
|
||||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
|
||||||
if ((head)->slh_first == (elm)) { \
|
|
||||||
SLIST_REMOVE_HEAD((head), field); \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
struct type *curelm = (head)->slh_first; \
|
|
||||||
while(curelm->field.sle_next != (elm)) \
|
|
||||||
curelm = curelm->field.sle_next; \
|
|
||||||
curelm->field.sle_next = \
|
|
||||||
curelm->field.sle_next->field.sle_next; \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List definitions.
|
|
||||||
*/
|
|
||||||
#define LIST_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *lh_first; /* first element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LIST_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define LIST_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *le_next; /* next element */ \
|
|
||||||
struct type **le_prev; /* address of previous next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List access methods.
|
|
||||||
*/
|
|
||||||
#define LIST_FIRST(head) ((head)->lh_first)
|
|
||||||
#define LIST_END(head) NULL
|
|
||||||
#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head))
|
|
||||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
|
||||||
|
|
||||||
#define LIST_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->lh_first); \
|
|
||||||
(var) != LIST_END(head); \
|
|
||||||
(var) = ((var)->field.le_next))
|
|
||||||
|
|
||||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = LIST_FIRST((head)); \
|
|
||||||
(var) != LIST_END(head) && \
|
|
||||||
((tvar) = LIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define LIST_MOVE(head1, head2) do { \
|
|
||||||
LIST_INIT((head2)); \
|
|
||||||
if (!LIST_EMPTY((head1))) { \
|
|
||||||
(head2)->lh_first = (head1)->lh_first; \
|
|
||||||
LIST_INIT((head1)); \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List functions.
|
|
||||||
*/
|
|
||||||
#if defined(QUEUEDEBUG)
|
|
||||||
#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \
|
|
||||||
if ((head)->lh_first && \
|
|
||||||
(head)->lh_first->field.le_prev != &(head)->lh_first) \
|
|
||||||
QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_LIST_OP(elm, field) \
|
|
||||||
if ((elm)->field.le_next && \
|
|
||||||
(elm)->field.le_next->field.le_prev != \
|
|
||||||
&(elm)->field.le_next) \
|
|
||||||
QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \
|
|
||||||
__FILE__, __LINE__); \
|
|
||||||
if (*(elm)->field.le_prev != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \
|
|
||||||
(elm)->field.le_next = (void *)1L; \
|
|
||||||
(elm)->field.le_prev = (void *)1L;
|
|
||||||
#else
|
|
||||||
#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_LIST_OP(elm, field)
|
|
||||||
#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LIST_INIT(head) do { \
|
|
||||||
(head)->lh_first = LIST_END(head); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_LIST_OP((listelm), field) \
|
|
||||||
if (((elm)->field.le_next = (listelm)->field.le_next) != \
|
|
||||||
LIST_END(head)) \
|
|
||||||
(listelm)->field.le_next->field.le_prev = \
|
|
||||||
&(elm)->field.le_next; \
|
|
||||||
(listelm)->field.le_next = (elm); \
|
|
||||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_LIST_OP((listelm), field) \
|
|
||||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
|
||||||
(elm)->field.le_next = (listelm); \
|
|
||||||
*(listelm)->field.le_prev = (elm); \
|
|
||||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \
|
|
||||||
if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\
|
|
||||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
|
||||||
(head)->lh_first = (elm); \
|
|
||||||
(elm)->field.le_prev = &(head)->lh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_REMOVE(elm, field) do { \
|
|
||||||
QUEUEDEBUG_LIST_OP((elm), field) \
|
|
||||||
if ((elm)->field.le_next != NULL) \
|
|
||||||
(elm)->field.le_next->field.le_prev = \
|
|
||||||
(elm)->field.le_prev; \
|
|
||||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
|
||||||
QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
|
||||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
|
||||||
(elm2)->field.le_next->field.le_prev = \
|
|
||||||
&(elm2)->field.le_next; \
|
|
||||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
|
||||||
*(elm2)->field.le_prev = (elm2); \
|
|
||||||
QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Simple queue definitions.
|
|
||||||
*/
|
|
||||||
#define SIMPLEQ_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *sqh_first; /* first element */ \
|
|
||||||
struct type **sqh_last; /* addr of last next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL, &(head).sqh_first }
|
|
||||||
|
|
||||||
#define SIMPLEQ_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *sqe_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Simple queue access methods.
|
|
||||||
*/
|
|
||||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
|
||||||
#define SIMPLEQ_END(head) NULL
|
|
||||||
#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head))
|
|
||||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
|
||||||
|
|
||||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->sqh_first); \
|
|
||||||
(var) != SIMPLEQ_END(head); \
|
|
||||||
(var) = ((var)->field.sqe_next))
|
|
||||||
|
|
||||||
#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \
|
|
||||||
for ((var) = ((head)->sqh_first); \
|
|
||||||
(var) != SIMPLEQ_END(head) && \
|
|
||||||
((next = ((var)->field.sqe_next)), 1); \
|
|
||||||
(var) = (next))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Simple queue functions.
|
|
||||||
*/
|
|
||||||
#define SIMPLEQ_INIT(head) do { \
|
|
||||||
(head)->sqh_first = NULL; \
|
|
||||||
(head)->sqh_last = &(head)->sqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
|
||||||
(head)->sqh_first = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
(elm)->field.sqe_next = NULL; \
|
|
||||||
*(head)->sqh_last = (elm); \
|
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
|
||||||
(listelm)->field.sqe_next = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
|
||||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
|
||||||
(head)->sqh_last = &(head)->sqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
|
||||||
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
|
|
||||||
== NULL) \
|
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
|
|
||||||
if ((head)->sqh_first == (elm)) { \
|
|
||||||
SIMPLEQ_REMOVE_HEAD((head), field); \
|
|
||||||
} else { \
|
|
||||||
struct type *curelm = (head)->sqh_first; \
|
|
||||||
while (curelm->field.sqe_next != (elm)) \
|
|
||||||
curelm = curelm->field.sqe_next; \
|
|
||||||
if ((curelm->field.sqe_next = \
|
|
||||||
curelm->field.sqe_next->field.sqe_next) == NULL) \
|
|
||||||
(head)->sqh_last = &(curelm)->field.sqe_next; \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_CONCAT(head1, head2) do { \
|
|
||||||
if (!SIMPLEQ_EMPTY((head2))) { \
|
|
||||||
*(head1)->sqh_last = (head2)->sqh_first; \
|
|
||||||
(head1)->sqh_last = (head2)->sqh_last; \
|
|
||||||
SIMPLEQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_LAST(head, type, field) \
|
|
||||||
(SIMPLEQ_EMPTY((head)) ? \
|
|
||||||
NULL : \
|
|
||||||
((struct type *)(void *) \
|
|
||||||
((char *)((head)->sqh_last) - offsetof(struct type, field))))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue definitions.
|
|
||||||
*/
|
|
||||||
#define _TAILQ_HEAD(name, type, qual) \
|
|
||||||
struct name { \
|
|
||||||
qual type *tqh_first; /* first element */ \
|
|
||||||
qual type *qual *tqh_last; /* addr of last next element */ \
|
|
||||||
}
|
|
||||||
#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
|
|
||||||
|
|
||||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ TAILQ_END(head), &(head).tqh_first }
|
|
||||||
|
|
||||||
#define _TAILQ_ENTRY(type, qual) \
|
|
||||||
struct { \
|
|
||||||
qual type *tqe_next; /* next element */ \
|
|
||||||
qual type *qual *tqe_prev; /* address of previous next element */\
|
|
||||||
}
|
|
||||||
#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue access methods.
|
|
||||||
*/
|
|
||||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
|
||||||
#define TAILQ_END(head) (NULL)
|
|
||||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
|
||||||
#define TAILQ_LAST(head, headname) \
|
|
||||||
(*(((struct headname *)(void *)((head)->tqh_last))->tqh_last))
|
|
||||||
#define TAILQ_PREV(elm, headname, field) \
|
|
||||||
(*(((struct headname *)(void *)((elm)->field.tqe_prev))->tqh_last))
|
|
||||||
#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head))
|
|
||||||
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->tqh_first); \
|
|
||||||
(var) != TAILQ_END(head); \
|
|
||||||
(var) = ((var)->field.tqe_next))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_SAFE(var, head, field, next) \
|
|
||||||
for ((var) = ((head)->tqh_first); \
|
|
||||||
(var) != TAILQ_END(head) && \
|
|
||||||
((next) = TAILQ_NEXT(var, field), 1); (var) = (next))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
|
||||||
for ((var) = TAILQ_LAST((head), headname); \
|
|
||||||
(var) != TAILQ_END(head); \
|
|
||||||
(var) = TAILQ_PREV((var), headname, field))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \
|
|
||||||
for ((var) = TAILQ_LAST((head), headname); \
|
|
||||||
(var) != TAILQ_END(head) && \
|
|
||||||
((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue functions.
|
|
||||||
*/
|
|
||||||
#if defined(QUEUEDEBUG)
|
|
||||||
#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \
|
|
||||||
if ((head)->tqh_first && \
|
|
||||||
(head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \
|
|
||||||
if (*(head)->tqh_last != NULL) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_TAILQ_OP(elm, field) \
|
|
||||||
if ((elm)->field.tqe_next && \
|
|
||||||
(elm)->field.tqe_next->field.tqe_prev != \
|
|
||||||
&(elm)->field.tqe_next) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \
|
|
||||||
__FILE__, __LINE__); \
|
|
||||||
if (*(elm)->field.tqe_prev != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \
|
|
||||||
if ((elm)->field.tqe_next == NULL && \
|
|
||||||
(head)->tqh_last != &(elm)->field.tqe_next) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\
|
|
||||||
(head), (elm), __FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \
|
|
||||||
(elm)->field.tqe_next = (void *)1L; \
|
|
||||||
(elm)->field.tqe_prev = (void *)1L;
|
|
||||||
#else
|
|
||||||
#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_TAILQ_OP(elm, field)
|
|
||||||
#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TAILQ_INIT(head) do { \
|
|
||||||
(head)->tqh_first = TAILQ_END(head); \
|
|
||||||
(head)->tqh_last = &(head)->tqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \
|
|
||||||
if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\
|
|
||||||
(head)->tqh_first->field.tqe_prev = \
|
|
||||||
&(elm)->field.tqe_next; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
|
||||||
(head)->tqh_first = (elm); \
|
|
||||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \
|
|
||||||
(elm)->field.tqe_next = TAILQ_END(head); \
|
|
||||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
|
||||||
*(head)->tqh_last = (elm); \
|
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_OP((listelm), field) \
|
|
||||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \
|
|
||||||
TAILQ_END(head)) \
|
|
||||||
(elm)->field.tqe_next->field.tqe_prev = \
|
|
||||||
&(elm)->field.tqe_next; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
|
||||||
(listelm)->field.tqe_next = (elm); \
|
|
||||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_OP((listelm), field) \
|
|
||||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
|
||||||
(elm)->field.tqe_next = (listelm); \
|
|
||||||
*(listelm)->field.tqe_prev = (elm); \
|
|
||||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \
|
|
||||||
QUEUEDEBUG_TAILQ_OP((elm), field) \
|
|
||||||
if (((elm)->field.tqe_next) != TAILQ_END(head)) \
|
|
||||||
(elm)->field.tqe_next->field.tqe_prev = \
|
|
||||||
(elm)->field.tqe_prev; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
|
||||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
|
||||||
QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
|
||||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \
|
|
||||||
TAILQ_END(head)) \
|
|
||||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
|
||||||
&(elm2)->field.tqe_next; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
|
||||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
|
||||||
*(elm2)->field.tqe_prev = (elm2); \
|
|
||||||
QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
|
||||||
if (!TAILQ_EMPTY(head2)) { \
|
|
||||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
|
||||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
|
||||||
(head1)->tqh_last = (head2)->tqh_last; \
|
|
||||||
TAILQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue declarations.
|
|
||||||
*/
|
|
||||||
#define STAILQ_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *stqh_first; /* first element */ \
|
|
||||||
struct type **stqh_last; /* addr of last next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL, &(head).stqh_first }
|
|
||||||
|
|
||||||
#define STAILQ_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *stqe_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue access methods.
|
|
||||||
*/
|
|
||||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
|
||||||
#define STAILQ_END(head) NULL
|
|
||||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
|
||||||
#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue functions.
|
|
||||||
*/
|
|
||||||
#define STAILQ_INIT(head) do { \
|
|
||||||
(head)->stqh_first = NULL; \
|
|
||||||
(head)->stqh_last = &(head)->stqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
|
|
||||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
|
||||||
(head)->stqh_first = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
(elm)->field.stqe_next = NULL; \
|
|
||||||
*(head)->stqh_last = (elm); \
|
|
||||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
|
|
||||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
|
||||||
(listelm)->field.stqe_next = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
|
||||||
if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
|
|
||||||
(head)->stqh_last = &(head)->stqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
|
||||||
if ((head)->stqh_first == (elm)) { \
|
|
||||||
STAILQ_REMOVE_HEAD((head), field); \
|
|
||||||
} else { \
|
|
||||||
struct type *curelm = (head)->stqh_first; \
|
|
||||||
while (curelm->field.stqe_next != (elm)) \
|
|
||||||
curelm = curelm->field.stqe_next; \
|
|
||||||
if ((curelm->field.stqe_next = \
|
|
||||||
curelm->field.stqe_next->field.stqe_next) == NULL) \
|
|
||||||
(head)->stqh_last = &(curelm)->field.stqe_next; \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->stqh_first); \
|
|
||||||
(var); \
|
|
||||||
(var) = ((var)->field.stqe_next))
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = STAILQ_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define STAILQ_CONCAT(head1, head2) do { \
|
|
||||||
if (!STAILQ_EMPTY((head2))) { \
|
|
||||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
|
||||||
(head1)->stqh_last = (head2)->stqh_last; \
|
|
||||||
STAILQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_LAST(head, type, field) \
|
|
||||||
(STAILQ_EMPTY((head)) ? \
|
|
||||||
NULL : \
|
|
||||||
((struct type *)(void *) \
|
|
||||||
((char *)((head)->stqh_last) - offsetof(struct type, field))))
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _KERNEL
|
|
||||||
/*
|
|
||||||
* Circular queue definitions. Do not use. We still keep the macros
|
|
||||||
* for compatibility but because of pointer aliasing issues their use
|
|
||||||
* is discouraged!
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* __launder_type(): We use this ugly hack to work around the the compiler
|
|
||||||
* noticing that two types may not alias each other and elide tests in code.
|
|
||||||
* We hit this in the CIRCLEQ macros when comparing 'struct name *' and
|
|
||||||
* 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC
|
|
||||||
* 4.8) declare these comparisons as always false, causing the code to
|
|
||||||
* not run as designed.
|
|
||||||
*
|
|
||||||
* This hack is only to be used for comparisons and thus can be fully const.
|
|
||||||
* Do not use for assignment.
|
|
||||||
*
|
|
||||||
* If we ever choose to change the ABI of the CIRCLEQ macros, we could fix
|
|
||||||
* this by changing the head/tail sentinal values, but see the note above
|
|
||||||
* this one.
|
|
||||||
*/
|
|
||||||
static __inline const void * __launder_type(const void *);
|
|
||||||
static __inline const void *
|
|
||||||
__launder_type(const void *__x)
|
|
||||||
{
|
|
||||||
__asm __volatile("" : "+r" (__x));
|
|
||||||
return __x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(QUEUEDEBUG)
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \
|
|
||||||
if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \
|
|
||||||
(head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__); \
|
|
||||||
if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \
|
|
||||||
(head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \
|
|
||||||
if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \
|
|
||||||
if ((head)->cqh_last != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \
|
|
||||||
(elm), __FILE__, __LINE__); \
|
|
||||||
} else { \
|
|
||||||
if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \
|
|
||||||
(elm), __FILE__, __LINE__); \
|
|
||||||
} \
|
|
||||||
if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \
|
|
||||||
if ((head)->cqh_first != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \
|
|
||||||
(elm), __FILE__, __LINE__); \
|
|
||||||
} else { \
|
|
||||||
if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \
|
|
||||||
(elm), __FILE__, __LINE__); \
|
|
||||||
}
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \
|
|
||||||
(elm)->field.cqe_next = (void *)1L; \
|
|
||||||
(elm)->field.cqe_prev = (void *)1L;
|
|
||||||
#else
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field)
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CIRCLEQ_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *cqh_first; /* first element */ \
|
|
||||||
struct type *cqh_last; /* last element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
|
||||||
|
|
||||||
#define CIRCLEQ_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *cqe_next; /* next element */ \
|
|
||||||
struct type *cqe_prev; /* previous element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Circular queue functions.
|
|
||||||
*/
|
|
||||||
#define CIRCLEQ_INIT(head) do { \
|
|
||||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
|
||||||
(head)->cqh_last = CIRCLEQ_END(head); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
|
|
||||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
|
||||||
(elm)->field.cqe_prev = (listelm); \
|
|
||||||
if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_last = (elm); \
|
|
||||||
else \
|
|
||||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
|
||||||
(listelm)->field.cqe_next = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
|
|
||||||
(elm)->field.cqe_next = (listelm); \
|
|
||||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
|
||||||
if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_first = (elm); \
|
|
||||||
else \
|
|
||||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
|
||||||
(listelm)->field.cqe_prev = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
|
||||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
|
||||||
if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_last = (elm); \
|
|
||||||
else \
|
|
||||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
|
||||||
(head)->cqh_first = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
|
||||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
|
||||||
if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_first = (elm); \
|
|
||||||
else \
|
|
||||||
(head)->cqh_last->field.cqe_next = (elm); \
|
|
||||||
(head)->cqh_last = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \
|
|
||||||
if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
|
||||||
else \
|
|
||||||
(elm)->field.cqe_next->field.cqe_prev = \
|
|
||||||
(elm)->field.cqe_prev; \
|
|
||||||
if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
|
||||||
else \
|
|
||||||
(elm)->field.cqe_prev->field.cqe_next = \
|
|
||||||
(elm)->field.cqe_next; \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->cqh_first); \
|
|
||||||
(var) != CIRCLEQ_ENDC(head); \
|
|
||||||
(var) = ((var)->field.cqe_next))
|
|
||||||
|
|
||||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
|
||||||
for ((var) = ((head)->cqh_last); \
|
|
||||||
(var) != CIRCLEQ_ENDC(head); \
|
|
||||||
(var) = ((var)->field.cqe_prev))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Circular queue access methods.
|
|
||||||
*/
|
|
||||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
|
||||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
|
||||||
/* For comparisons */
|
|
||||||
#define CIRCLEQ_ENDC(head) (__launder_type(head))
|
|
||||||
/* For assignments */
|
|
||||||
#define CIRCLEQ_END(head) ((void *)(head))
|
|
||||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
|
||||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
|
||||||
#define CIRCLEQ_EMPTY(head) \
|
|
||||||
(CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head))
|
|
||||||
|
|
||||||
#define CIRCLEQ_LOOP_NEXT(head, elm, field) \
|
|
||||||
(((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
|
|
||||||
? ((head)->cqh_first) \
|
|
||||||
: (elm->field.cqe_next))
|
|
||||||
#define CIRCLEQ_LOOP_PREV(head, elm, field) \
|
|
||||||
(((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
|
|
||||||
? ((head)->cqh_last) \
|
|
||||||
: (elm->field.cqe_prev))
|
|
||||||
#endif /* !_KERNEL */
|
|
||||||
|
|
||||||
#endif /* !_SYS_QUEUE_H_ */
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#ifndef _SYS_REDOX_H
|
|
||||||
#define _SYS_REDOX_H
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __redox__
|
|
||||||
|
|
||||||
ssize_t redox_fpath(int fd, void * buf, size_t count);
|
|
||||||
void * redox_physalloc(size_t size);
|
|
||||||
int redox_physfree(void * physical_address, size_t size);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// From musl, license MIT
|
|
||||||
#ifndef _SYS_SYSMACROS_H
|
|
||||||
#define _SYS_SYSMACROS_H
|
|
||||||
|
|
||||||
#define major(x) \
|
|
||||||
((unsigned)( (((x)>>31>>1) & 0xfffff000) | (((x)>>8) & 0x00000fff) ))
|
|
||||||
#define minor(x) \
|
|
||||||
((unsigned)( (((x)>>12) & 0xffffff00) | ((x) & 0x000000ff) ))
|
|
||||||
|
|
||||||
#define makedev(x,y) ( \
|
|
||||||
(((x)&0xfffff000ULL) << 32) | \
|
|
||||||
(((x)&0x00000fffULL) << 8) | \
|
|
||||||
(((y)&0xffffff00ULL) << 12) | \
|
|
||||||
(((y)&0x000000ffULL)) )
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifndef _SYS_USER_H
|
|
||||||
#define _SYS_USER_H
|
|
||||||
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
|
|
||||||
#include <arch/x64/user.h>
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
#include <arch/aarch64/user.h>
|
|
||||||
#elif defined(__riscv) && __riscv_xlen==64
|
|
||||||
#include <arch/riscv64/user.h>
|
|
||||||
#else
|
|
||||||
#error "Unknown architecture"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef _RELIBC_SYS_VFS_H
|
|
||||||
#define _RELIBC_SYS_VFS_H
|
|
||||||
|
|
||||||
/* <sys/vfs.h> is the historical Linux/BSD name for the filesystem-statistics
|
|
||||||
* interface; glibc ships it as a thin wrapper that simply re-exports
|
|
||||||
* <sys/statfs.h> (struct statfs + statfs()/fstatfs()). Mirror that so code that
|
|
||||||
* includes <sys/vfs.h> gets the same declarations. */
|
|
||||||
#include <sys/statfs.h>
|
|
||||||
|
|
||||||
#endif /* _RELIBC_SYS_VFS_H */
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <sys/syslog.h>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ld_so"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "ld_so"
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![feature(linkage)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use core::arch::global_asm;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.weak _DYNAMIC
|
|
||||||
.hidden _DYNAMIC
|
|
||||||
|
|
||||||
.global _start
|
|
||||||
_start:
|
|
||||||
mov x28, sp
|
|
||||||
// align stack to 16 bytes
|
|
||||||
and sp, x28, #0xfffffffffffffff0
|
|
||||||
adr x1, _start
|
|
||||||
mov x0, x28
|
|
||||||
adrp x2, _DYNAMIC
|
|
||||||
add x2, x2, #:lo12:_DYNAMIC
|
|
||||||
// ld_so_start(stack=x0, ld_entry=x1, dynamic=x2)
|
|
||||||
bl relibc_ld_so_start
|
|
||||||
// restore original stack, clear registers, and jump to the new start function
|
|
||||||
mov sp, x28
|
|
||||||
mov x1, xzr
|
|
||||||
mov x2, xzr
|
|
||||||
mov x3, xzr
|
|
||||||
mov x4, xzr
|
|
||||||
mov x5, xzr
|
|
||||||
mov x6, xzr
|
|
||||||
mov x7, xzr
|
|
||||||
mov x8, xzr
|
|
||||||
mov x9, xzr
|
|
||||||
mov x10, xzr
|
|
||||||
mov x11, xzr
|
|
||||||
mov x12, xzr
|
|
||||||
mov x13, xzr
|
|
||||||
mov x14, xzr
|
|
||||||
mov x15, xzr
|
|
||||||
mov x16, xzr
|
|
||||||
mov x17, xzr
|
|
||||||
mov x18, xzr
|
|
||||||
mov x19, xzr
|
|
||||||
mov x20, xzr
|
|
||||||
mov x21, xzr
|
|
||||||
mov x22, xzr
|
|
||||||
mov x23, xzr
|
|
||||||
mov x24, xzr
|
|
||||||
mov x25, xzr
|
|
||||||
mov x26, xzr
|
|
||||||
mov x27, xzr
|
|
||||||
mov x28, xzr
|
|
||||||
mov x29, xzr
|
|
||||||
mov x30, xzr
|
|
||||||
br x0
|
|
||||||
udf #0
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
push esp
|
|
||||||
call relibc_ld_so_start
|
|
||||||
pop esp
|
|
||||||
# TODO: x86
|
|
||||||
ud2
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.weak _DYNAMIC
|
|
||||||
.hidden _DYNAMIC
|
|
||||||
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
lea rsi, [rip + _start]
|
|
||||||
|
|
||||||
# Save original stack and align stack to 16 bytes
|
|
||||||
mov rbp, rsp
|
|
||||||
and rsp, 0xfffffffffffffff0
|
|
||||||
|
|
||||||
# Call ld_so_start(stack=rdi, ld_entry=rsi, dynamic=rdx)
|
|
||||||
mov rdi, rbp
|
|
||||||
lea rdx, [rip + _DYNAMIC]
|
|
||||||
call relibc_ld_so_start
|
|
||||||
|
|
||||||
# Restore original stack, clear registers, and jump to new start function
|
|
||||||
mov rsp, rbp
|
|
||||||
xor rcx, rcx
|
|
||||||
xor rdx, rdx
|
|
||||||
xor rdi, rdi
|
|
||||||
xor rsi, rsi
|
|
||||||
xor r8, r8
|
|
||||||
xor r9, r9
|
|
||||||
xor r10, r10
|
|
||||||
xor r11, r11
|
|
||||||
fninit
|
|
||||||
jmp rax
|
|
||||||
ud2
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
mv a0, sp
|
|
||||||
call relibc_ld_so_start
|
|
||||||
unimp
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn main(_argc: isize, _argv: *const *const i8) -> usize {
|
|
||||||
// LD
|
|
||||||
0x1D
|
|
||||||
}
|
|
||||||
|
|
||||||
#[linkage = "weak"]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn relibc_panic(_pi: &::core::panic::PanicInfo) -> ! {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
#[linkage = "weak"]
|
|
||||||
pub unsafe fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
|
|
||||||
relibc_panic(pi)
|
|
||||||
}
|
|
||||||
-1
Submodule openlibm deleted from f5ed774593
@@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "redox-ioctl"
|
|
||||||
authors = ["bjorn3 <bjorn3_gh@protonmail.com>"]
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
license = "MIT"
|
|
||||||
description = "Ioctl definitions and (de)serialization for Redox"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
drm-sys = "0.8.0"
|
|
||||||
redox_syscall = "0.9.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,347 +0,0 @@
|
|||||||
use alloc::vec::Vec;
|
|
||||||
use core::{
|
|
||||||
cmp,
|
|
||||||
ffi::{c_char, c_int, c_uint},
|
|
||||||
iter, mem, slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use drm_sys::{
|
|
||||||
__kernel_size_t, DRM_PROP_NAME_LEN, drm_clip_rect, drm_get_cap, drm_mode_card_res,
|
|
||||||
drm_mode_closefb, drm_mode_connector_set_property, drm_mode_create_dumb, drm_mode_crtc,
|
|
||||||
drm_mode_crtc_page_flip_target, drm_mode_cursor, drm_mode_cursor2, drm_mode_destroy_dumb,
|
|
||||||
drm_mode_fb_cmd, drm_mode_fb_cmd2, drm_mode_fb_dirty_cmd, drm_mode_get_blob,
|
|
||||||
drm_mode_get_connector, drm_mode_get_encoder, drm_mode_get_plane, drm_mode_get_plane_res,
|
|
||||||
drm_mode_get_property, drm_mode_map_dumb, drm_mode_modeinfo, drm_mode_obj_get_properties,
|
|
||||||
drm_mode_property_enum, drm_mode_set_plane, drm_set_client_cap, drm_set_version, drm_unique,
|
|
||||||
drm_version,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const VERSION: u64 = 0;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_version, DrmVersion {
|
|
||||||
version_major: c_int,
|
|
||||||
version_minor: c_int,
|
|
||||||
version_patchlevel: c_int,
|
|
||||||
name_len: __kernel_size_t,
|
|
||||||
name: *mut c_char [array<c_char, name_len>],
|
|
||||||
date_len: __kernel_size_t,
|
|
||||||
date: *mut c_char [array<c_char, date_len>],
|
|
||||||
desc_len: __kernel_size_t,
|
|
||||||
desc: *mut c_char [array<c_char, desc_len>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const GET_UNIQUE: u64 = 0x01;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_unique, DrmUnique {
|
|
||||||
unique_len: __kernel_size_t,
|
|
||||||
unique: *mut c_char [array<c_char, unique_len>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const SET_VERSION: u64 = 0x07;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_set_version, DrmSetVersion {
|
|
||||||
drm_di_major: c_int,
|
|
||||||
drm_di_minor: c_int,
|
|
||||||
drm_dd_major: c_int,
|
|
||||||
drm_dd_minor: c_int,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const GET_CAP: u64 = 0x0C;
|
|
||||||
pub use drm_sys::DRM_CAP_DUMB_BUFFER;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_get_cap, DrmGetCap {
|
|
||||||
capability: u64,
|
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const SET_CLIENT_CAP: u64 = 0x0D;
|
|
||||||
pub use drm_sys::DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_set_client_cap, DrmSetClientCap {
|
|
||||||
capability: u64,
|
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CARD_RES: u64 = 0xA0;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_card_res, DrmModeCardRes {
|
|
||||||
fb_id_ptr: u64 [array<u32, count_fbs>],
|
|
||||||
crtc_id_ptr: u64 [array<u32, count_crtcs>],
|
|
||||||
connector_id_ptr: u64 [array<u32, count_connectors>],
|
|
||||||
encoder_id_ptr: u64 [array<u32, count_encoders>],
|
|
||||||
count_fbs: u32,
|
|
||||||
count_crtcs: u32,
|
|
||||||
count_connectors: u32,
|
|
||||||
count_encoders: u32,
|
|
||||||
min_width: u32,
|
|
||||||
max_width: u32,
|
|
||||||
min_height: u32,
|
|
||||||
max_height: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_CRTC: u64 = 0xA1;
|
|
||||||
pub const MODE_SET_CRTC: u64 = 0xA2;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_crtc, DrmModeCrtc {
|
|
||||||
set_connectors_ptr: u64 [array<u32, count_connectors>],
|
|
||||||
count_connectors: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
fb_id: u32,
|
|
||||||
x: u32,
|
|
||||||
y: u32,
|
|
||||||
gamma_size: u32,
|
|
||||||
mode_valid: u32,
|
|
||||||
mode: drm_mode_modeinfo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CURSOR: u64 = 0xA3;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_cursor, DrmModeCursor {
|
|
||||||
flags: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
x: i32,
|
|
||||||
y:i32,
|
|
||||||
width:u32,
|
|
||||||
height:u32,
|
|
||||||
handle:u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_ENCODER: u64 = 0xA6;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_encoder, DrmModeGetEncoder {
|
|
||||||
encoder_id: u32,
|
|
||||||
encoder_type: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
possible_crtcs: u32,
|
|
||||||
possible_clones: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_CONNECTOR: u64 = 0xA7;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_connector, DrmModeGetConnector {
|
|
||||||
encoders_ptr: u64 [array<u32, count_encoders>],
|
|
||||||
modes_ptr: u64 [array<drm_mode_modeinfo, count_modes>],
|
|
||||||
props_ptr: u64 [array<u32, count_props>],
|
|
||||||
prop_values_ptr: u64 [array<u64, count_props>],
|
|
||||||
count_modes: u32,
|
|
||||||
count_props: u32,
|
|
||||||
count_encoders: u32,
|
|
||||||
encoder_id: u32,
|
|
||||||
connector_id: u32,
|
|
||||||
connector_type: u32,
|
|
||||||
connector_type_id: u32,
|
|
||||||
connection: u32,
|
|
||||||
mm_width: u32,
|
|
||||||
mm_height: u32,
|
|
||||||
subpixel: u32,
|
|
||||||
pad: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_PROPERTY: u64 = 0xAA;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_property, DrmModeGetProperty {
|
|
||||||
values_ptr: u64 [array<u64, count_values>],
|
|
||||||
enum_blob_ptr: u64 [array<drm_mode_property_enum, count_enum_blobs>],
|
|
||||||
prop_id: u32,
|
|
||||||
flags: u32,
|
|
||||||
name: [c_char; DRM_PROP_NAME_LEN as usize],
|
|
||||||
count_values: u32,
|
|
||||||
count_enum_blobs: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_SET_PROPERTY: u64 = 0xAB;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_connector_set_property, DrmModeConnectorSetProperty {
|
|
||||||
value: u64,
|
|
||||||
prop_id: u32,
|
|
||||||
connector_id: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_PROP_BLOB: u64 = 0xAC;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_blob, DrmModeGetBlob {
|
|
||||||
blob_id: u32,
|
|
||||||
length: u32,
|
|
||||||
data: u64 [array<u8, length>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_FB: u64 = 0xAD;
|
|
||||||
pub const MODE_ADD_FB: u64 = 0xAE;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_fb_cmd, DrmModeFbCmd {
|
|
||||||
fb_id: u32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
pitch: u32,
|
|
||||||
bpp: u32,
|
|
||||||
depth: u32,
|
|
||||||
handle: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_RM_FB: u64 = 0xAF;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct standin_for_uint, StandinForUint {
|
|
||||||
inner: c_uint,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub struct standin_for_uint {
|
|
||||||
pub inner: c_uint,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_PAGE_FLIP: u64 = 0xB0;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_crtc_page_flip_target, DrmModeCrtcPageFlipTarget {
|
|
||||||
crtc_id: u32,
|
|
||||||
fb_id: u32,
|
|
||||||
flags: u32,
|
|
||||||
sequence: u32,
|
|
||||||
user_data: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_DIRTYFB: u64 = 0xB1;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_fb_dirty_cmd, DrmModeFbDirtyCmd {
|
|
||||||
fb_id: u32,
|
|
||||||
flags: u32,
|
|
||||||
color: u32,
|
|
||||||
num_clips: u32,
|
|
||||||
clips_ptr: u64 [array<drm_clip_rect, num_clips>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CREATE_DUMB: u64 = 0xB2;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_create_dumb, DrmModeCreateDumb {
|
|
||||||
height: u32,
|
|
||||||
width: u32,
|
|
||||||
bpp: u32,
|
|
||||||
flags: u32,
|
|
||||||
handle: u32,
|
|
||||||
pitch: u32,
|
|
||||||
size: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_MAP_DUMB: u64 = 0xB3;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_map_dumb, DrmModeMapDumb {
|
|
||||||
handle: u32,
|
|
||||||
pad: u32,
|
|
||||||
offset: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_DESTROY_DUMB: u64 = 0xB4;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_destroy_dumb, DrmModeDestroyDumb {
|
|
||||||
handle: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_PLANE_RES: u64 = 0xB5;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_plane_res, DrmModeGetPlaneRes {
|
|
||||||
plane_id_ptr: u64 [array<u32, count_planes>],
|
|
||||||
count_planes: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_PLANE: u64 = 0xB6;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_plane, DrmModeGetPlane {
|
|
||||||
plane_id: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
fb_id: u32,
|
|
||||||
possible_crtcs: u32,
|
|
||||||
gamma_size: u32,
|
|
||||||
count_format_types: u32,
|
|
||||||
format_type_ptr: u64 [array<u32, count_format_types>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_SET_PLANE: u64 = 0xB7;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_set_plane, DrmModeSetPlane {
|
|
||||||
plane_id: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
fb_id: u32,
|
|
||||||
flags: u32,
|
|
||||||
crtc_x: i32,
|
|
||||||
crtc_y: i32,
|
|
||||||
crtc_w: u32,
|
|
||||||
crtc_h: u32,
|
|
||||||
src_x: u32,
|
|
||||||
src_y: u32,
|
|
||||||
src_h: u32,
|
|
||||||
src_w: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_ADD_FB2: u64 = 0xB8;
|
|
||||||
|
|
||||||
pub const MODE_OBJ_GET_PROPERTIES: u64 = 0xB9;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_obj_get_properties, DrmModeObjGetProperties {
|
|
||||||
props_ptr: u64 [array<u32, count_props>],
|
|
||||||
prop_values_ptr: u64 [array<u64, count_props>],
|
|
||||||
count_props: u32,
|
|
||||||
obj_id: u32,
|
|
||||||
obj_type: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CURSOR2: u64 = 0xBB;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_cursor2, DrmModeCursor2 {
|
|
||||||
flags: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
handle: u32,
|
|
||||||
hot_x: i32,
|
|
||||||
hot_y: i32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_FB2: u64 = 0xCE;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_fb_cmd2, DrmModeFbCmd2 {
|
|
||||||
fb_id: u32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
pixel_format: u32,
|
|
||||||
flags: u32,
|
|
||||||
handles: [u32; 4],
|
|
||||||
pitches: [u32; 4],
|
|
||||||
offsets: [u32; 4],
|
|
||||||
modifier: [u64; 4],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CLOSE_FB: u64 = 0xD0;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_closefb, DrmModeClosefb {
|
|
||||||
fb_id: u32,
|
|
||||||
pad: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
pub trait IoctlData {
|
|
||||||
unsafe fn write(&self) -> Vec<u8>;
|
|
||||||
unsafe fn read_from(&mut self, buf: &[u8]);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! define_ioctl_data {
|
|
||||||
(struct $ioctl_ty:ident, $mem_ty:ident {
|
|
||||||
$($rest:tt)*
|
|
||||||
}) => {
|
|
||||||
define_ioctl_data!(
|
|
||||||
struct $ioctl_ty, $mem_ty { $($rest)* } => (), (), ()
|
|
||||||
);
|
|
||||||
};
|
|
||||||
(struct $ioctl_ty:ident, $mem_ty:ident {
|
|
||||||
$field:ident: $ty:ty,
|
|
||||||
$($rest:tt)*
|
|
||||||
} =>
|
|
||||||
($($ioctl_fields:tt)*),
|
|
||||||
($($counted_fields:tt)*),
|
|
||||||
($($noncounted_fields:tt)*)
|
|
||||||
) => {
|
|
||||||
define_ioctl_data!(
|
|
||||||
struct $ioctl_ty, $mem_ty { $($rest)* } =>
|
|
||||||
($($ioctl_fields)* $field: $ty,),
|
|
||||||
($($counted_fields)*),
|
|
||||||
($($noncounted_fields)* $field: $ty,)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
(struct $ioctl_ty:ident, $mem_ty:ident {
|
|
||||||
$field:ident: $ty:ty [array<$el:ty, $counted_by:ident>],
|
|
||||||
$($rest:tt)*
|
|
||||||
} =>
|
|
||||||
($($ioctl_fields:tt)*),
|
|
||||||
($($counted_fields:tt)*),
|
|
||||||
($($noncounted_fields:tt)*)
|
|
||||||
) => {
|
|
||||||
define_ioctl_data!(
|
|
||||||
struct $ioctl_ty, $mem_ty { $($rest)* } =>
|
|
||||||
($($ioctl_fields)* $field: $ty,),
|
|
||||||
($($counted_fields)* $field: $ty [array<$el, $counted_by>],),
|
|
||||||
($($noncounted_fields)*)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
(struct $ioctl_ty:ident, $mem_ty:ident {} =>
|
|
||||||
($($ioctl_field:ident: $ioctl_field_ty:ty,)*),
|
|
||||||
($($counted_field:ident: $counted_ty:ty [array<$el:ty, $counted_by:ident>],)*),
|
|
||||||
($($noncounted_field:ident: $noncounted_ty:ty,)*)
|
|
||||||
) => {
|
|
||||||
// FIXME check ioctl_ty doesn't have padding
|
|
||||||
const _: $ioctl_ty = $ioctl_ty {
|
|
||||||
$($ioctl_field: unsafe { mem::zeroed::<$ioctl_field_ty>() },)*
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ${concat(__, $mem_ty, Noncounted)} {
|
|
||||||
$($noncounted_field: $noncounted_ty,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct $mem_ty<'a> {
|
|
||||||
noncounted_fields: &'a mut ${concat(__, $mem_ty, Noncounted)},
|
|
||||||
$($counted_field: &'a mut [$el],)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $crate::ioctl_data::IoctlData for $ioctl_ty {
|
|
||||||
unsafe fn write(&self) -> Vec<u8> {
|
|
||||||
let noncounted_fields = ${concat(__, $mem_ty, Noncounted)} {
|
|
||||||
$($noncounted_field: self.$noncounted_field,)*
|
|
||||||
};
|
|
||||||
// FIXME use Vec::with_capacity
|
|
||||||
let mut data = Vec::<u8>::new();
|
|
||||||
data.extend_from_slice(&unsafe {
|
|
||||||
mem::transmute::<
|
|
||||||
${concat(__, $mem_ty, Noncounted)},
|
|
||||||
[u8; size_of::<${concat(__, $mem_ty, Noncounted)}>()],
|
|
||||||
>(noncounted_fields)
|
|
||||||
});
|
|
||||||
$(
|
|
||||||
let size = self.$counted_by as usize * size_of::<$el>();
|
|
||||||
if self.$counted_field as usize != 0 {
|
|
||||||
let $counted_field = unsafe {
|
|
||||||
slice::from_raw_parts(self.$counted_field as *const u8, size)
|
|
||||||
};
|
|
||||||
data.extend_from_slice(&$counted_field);
|
|
||||||
} else {
|
|
||||||
data.extend(iter::repeat(0u8).take(size));
|
|
||||||
};
|
|
||||||
|
|
||||||
)*
|
|
||||||
data
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn read_from(&mut self, mut buf: &[u8]) {
|
|
||||||
// FIXME be robust against malicious scheme implementations by returning an error
|
|
||||||
// when the buf is the wrong size
|
|
||||||
let noncounted_fields = buf.split_off(..size_of::<${concat(__, $mem_ty, Noncounted)}>()).unwrap();
|
|
||||||
|
|
||||||
$(
|
|
||||||
let size = self.$counted_by as usize * size_of::<$el>();
|
|
||||||
let $counted_field = buf.split_off(..size).unwrap();
|
|
||||||
if self.$counted_field as usize != 0 {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(self.$counted_field as *mut u8, size).copy_from_slice($counted_field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
|
|
||||||
assert!(buf.is_empty());
|
|
||||||
|
|
||||||
let noncounted_fields = unsafe { &*core::ptr::from_ref(noncounted_fields).cast::<${concat(__, $mem_ty, Noncounted)}>() };
|
|
||||||
$(self.$noncounted_field = noncounted_fields.$noncounted_field;)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> $mem_ty<'a> {
|
|
||||||
pub fn with(
|
|
||||||
mut buf: &'a mut [u8],
|
|
||||||
f: impl FnOnce($mem_ty<'a>) -> syscall::Result<usize>,
|
|
||||||
) -> syscall::Result<usize> {
|
|
||||||
let noncounted_fields = buf.split_off_mut(..size_of::<${concat(__, $mem_ty, Noncounted)}>())
|
|
||||||
.ok_or(syscall::Error::new(syscall::EINVAL))?;
|
|
||||||
let noncounted_fields = unsafe { &mut *core::ptr::from_mut(noncounted_fields).cast::<${concat(__, $mem_ty, Noncounted)}>() };
|
|
||||||
|
|
||||||
$(
|
|
||||||
let $counted_field = buf.split_off_mut(..noncounted_fields.$counted_by as usize * size_of::<$el>())
|
|
||||||
.ok_or(syscall::Error::new(syscall::EINVAL))?;
|
|
||||||
let $counted_field = unsafe {
|
|
||||||
slice::from_raw_parts_mut(core::ptr::from_mut($counted_field).cast::<$el>(), noncounted_fields.$counted_by as usize)
|
|
||||||
};
|
|
||||||
)*
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
return Err(syscall::Error::new(syscall::EINVAL));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
f($mem_ty {
|
|
||||||
noncounted_fields,
|
|
||||||
$($counted_field,)*
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
pub fn $noncounted_field(&self) -> $noncounted_ty {
|
|
||||||
self.noncounted_fields.$noncounted_field
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Should not be called for fields used as array length
|
|
||||||
pub fn ${concat(set_, $noncounted_field)}(&mut self, data: $noncounted_ty) {
|
|
||||||
self.noncounted_fields.$noncounted_field = data;
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
|
|
||||||
$(
|
|
||||||
pub fn $counted_field(&self) -> &[$el] {
|
|
||||||
self.$counted_field
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ${concat(set_, $counted_field)}(&mut self, data: &[$el]) {
|
|
||||||
let copied_count = cmp::min(data.len(), self.$counted_field.len());
|
|
||||||
self.$counted_field[..copied_count].copy_from_slice(&data[..copied_count]);
|
|
||||||
self.noncounted_fields.$counted_by = data.len() as _;
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#![feature(macro_metavar_expr_concat)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod ioctl_data;
|
|
||||||
pub use ioctl_data::IoctlData;
|
|
||||||
|
|
||||||
pub mod drm;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "redox-rt"
|
|
||||||
authors = ["4lDO2 <4lDO2@protonmail.com>"]
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
license = "MIT"
|
|
||||||
description = "Libc-independent runtime for Redox"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bitflags.workspace = true
|
|
||||||
goblin = { version = "0.10", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
|
|
||||||
plain.workspace = true
|
|
||||||
ioslice.workspace = true
|
|
||||||
redox_syscall.workspace = true
|
|
||||||
redox-path.workspace = true
|
|
||||||
redox_protocols.workspace = true
|
|
||||||
|
|
||||||
generic-rt = { path = "../generic-rt" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
proc = []
|
|
||||||
default = ["proc"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull};
|
|
||||||
|
|
||||||
use syscall::{data::*, error::*};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Tcb,
|
|
||||||
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
||||||
signal::{PosixStackt, RtSigarea, SigStack, inner_c},
|
|
||||||
};
|
|
||||||
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
||||||
|
|
||||||
use super::ForkScratchpad;
|
|
||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
||||||
pub const STACK_TOP: usize = 1 << 47;
|
|
||||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SigArea {
|
|
||||||
pub altstack_top: usize,
|
|
||||||
pub altstack_bottom: usize,
|
|
||||||
pub tmp_x1_x2: [usize; 2],
|
|
||||||
pub tmp_x3_x4: [usize; 2],
|
|
||||||
pub tmp_x5_x6: [usize; 2],
|
|
||||||
pub tmp_x7_x8: [usize; 2],
|
|
||||||
pub tmp_sp: usize,
|
|
||||||
pub onstack: u64,
|
|
||||||
pub disable_signals_depth: u64,
|
|
||||||
pub pctl: usize, // TODO: remove
|
|
||||||
pub last_sig_was_restart: bool,
|
|
||||||
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
||||||
pub tmp_rt_inf: RtSigInfo,
|
|
||||||
pub tmp_id_inf: u64,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct ArchIntRegs {
|
|
||||||
pub x30: usize,
|
|
||||||
pub x29: usize,
|
|
||||||
pub x28: usize,
|
|
||||||
pub x27: usize,
|
|
||||||
pub x26: usize,
|
|
||||||
pub x25: usize,
|
|
||||||
pub x24: usize,
|
|
||||||
pub x23: usize,
|
|
||||||
pub x22: usize,
|
|
||||||
pub x21: usize,
|
|
||||||
pub x20: usize,
|
|
||||||
pub x19: usize,
|
|
||||||
pub x18: usize,
|
|
||||||
pub x17: usize,
|
|
||||||
pub x16: usize,
|
|
||||||
pub x15: usize,
|
|
||||||
pub x14: usize,
|
|
||||||
pub x13: usize,
|
|
||||||
pub x12: usize,
|
|
||||||
pub x11: usize,
|
|
||||||
pub x10: usize,
|
|
||||||
pub x9: usize,
|
|
||||||
pub x8: usize,
|
|
||||||
pub x7: usize,
|
|
||||||
pub x6: usize,
|
|
||||||
pub x5: usize,
|
|
||||||
pub x4: usize,
|
|
||||||
pub x3: usize,
|
|
||||||
pub x2: usize,
|
|
||||||
pub x1: usize,
|
|
||||||
|
|
||||||
pub sp: usize,
|
|
||||||
pub nzcv: usize, // user-accessible PSTATE bits
|
|
||||||
|
|
||||||
pub pc: usize,
|
|
||||||
pub x0: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
||||||
/// is already initialized as if it was a thread.
|
|
||||||
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file = open_via_dup.dup_into_upper(b"regs/env")?;
|
|
||||||
|
|
||||||
env.tpidr_el0 = 0;
|
|
||||||
|
|
||||||
file.write(&mut env)?;
|
|
||||||
crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
||||||
Error::mux(fork_inner(initial_rsp, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) {
|
|
||||||
//let _ = syscall::write(1, alloc::format!("CUR{cur_filetable_fd}PROC{new_proc_fd}THR{new_thr_fd}\n").as_bytes());
|
|
||||||
let _ = crate::sys::close(scratchpad.cur_filetable_fd);
|
|
||||||
unsafe {
|
|
||||||
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
||||||
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
||||||
},
|
|
||||||
new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd)
|
|
||||||
.to_upper()
|
|
||||||
.unwrap(),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
||||||
stp x29, x30, [sp, #-16]!
|
|
||||||
stp x27, x28, [sp, #-16]!
|
|
||||||
stp x25, x26, [sp, #-16]!
|
|
||||||
stp x23, x24, [sp, #-16]!
|
|
||||||
stp x21, x22, [sp, #-16]!
|
|
||||||
stp x19, x20, [sp, #-16]!
|
|
||||||
|
|
||||||
//TODO: store floating point regs
|
|
||||||
|
|
||||||
// x0: &ForkArgs
|
|
||||||
mov x1, sp
|
|
||||||
bl {fork_impl}
|
|
||||||
|
|
||||||
ldp x19, x20, [sp], #16
|
|
||||||
ldp x21, x22, [sp], #16
|
|
||||||
ldp x23, x24, [sp], #16
|
|
||||||
ldp x25, x26, [sp], #16
|
|
||||||
ldp x27, x28, [sp], #16
|
|
||||||
ldp x29, x30, [sp], #16
|
|
||||||
ret
|
|
||||||
"] <= [fork_impl = sym fork_impl]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_ret: ["
|
|
||||||
# scratchpad is in x1, move to x0 for child_hook
|
|
||||||
mov x0, x1
|
|
||||||
|
|
||||||
bl {child_hook}
|
|
||||||
|
|
||||||
//TODO: load floating point regs
|
|
||||||
|
|
||||||
mov x0, xzr
|
|
||||||
|
|
||||||
ldp x19, x20, [sp], #16
|
|
||||||
ldp x21, x22, [sp], #16
|
|
||||||
ldp x23, x24, [sp], #16
|
|
||||||
ldp x25, x26, [sp], #16
|
|
||||||
ldp x27, x28, [sp], #16
|
|
||||||
ldp x29, x30, [sp], #16
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= [child_hook = sym child_hook]);
|
|
||||||
|
|
||||||
// https://devblogs.microsoft.com/oldnewthing/20220811-00/?p=106963
|
|
||||||
asmfunction!(__relibc_internal_sigentry: ["
|
|
||||||
// Clear any active reservation.
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// The old pc and x0 are saved in the sigcontrol struct.
|
|
||||||
mrs x0, tpidr_el0 // ABI ptr
|
|
||||||
ldr x0, [x0] // TCB ptr
|
|
||||||
|
|
||||||
// Save x1-x6 and sp
|
|
||||||
stp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}]
|
|
||||||
stp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}]
|
|
||||||
stp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}]
|
|
||||||
stp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}]
|
|
||||||
mov x1, sp
|
|
||||||
str x1, [x0, #{tcb_sa_off} + {sa_tmp_sp}]
|
|
||||||
|
|
||||||
ldr x6, [x0, #{tcb_sa_off} + {sa_pctl}]
|
|
||||||
1:
|
|
||||||
// Load x1 with the thread's bits
|
|
||||||
add x5, x0, #{tcb_sc_off} + {sc_word}
|
|
||||||
ldaxr x1, [x5]
|
|
||||||
|
|
||||||
// First check if there are standard thread signals,
|
|
||||||
and x4, x1, x1, lsr #32 // x4 := x1 & (x1 >> 32)
|
|
||||||
cbnz x4, 3f // jump if x4 != 0
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// and if not, load process pending bitset.
|
|
||||||
add x5, x6, #{pctl_pending}
|
|
||||||
ldaxr x2, [x5]
|
|
||||||
|
|
||||||
// Check if there are standard proc signals:
|
|
||||||
lsr x3, x1, #32 // mask
|
|
||||||
and w3, w2, w3 // pending unblocked proc
|
|
||||||
cbz w3, 4f // skip 'fetch_andn' step if zero
|
|
||||||
|
|
||||||
// If there was one, find which one, and try clearing the bit (last value in x3, addr in x6)
|
|
||||||
// this picks the MSB rather than the LSB, unlike x86. POSIX does not require any specific
|
|
||||||
// ordering though.
|
|
||||||
clz w3, w3
|
|
||||||
mov w4, #31
|
|
||||||
sub w3, w4, w3
|
|
||||||
// x3 now contains the sig_idx
|
|
||||||
|
|
||||||
mov x4, #1
|
|
||||||
lsl x4, x4, x3 // bit to remove
|
|
||||||
|
|
||||||
sub x4, x2, x4 // bit was certainly set, so sub is allowed
|
|
||||||
// x4 is now the new mask to be set
|
|
||||||
add x5, x6, #{pctl_pending}
|
|
||||||
|
|
||||||
add x2, x5, #{pctl_sender_infos}
|
|
||||||
add x2, x2, w3, uxtb 3
|
|
||||||
ldar x2, [x2]
|
|
||||||
|
|
||||||
// Try clearing the bit, retrying on failure.
|
|
||||||
stxr w1, x4, [x5] // try setting pending set to x4, set w1 := 0 on success
|
|
||||||
cbnz w1, 1b // retry everything if this fails
|
|
||||||
mov x1, x3
|
|
||||||
b 2f
|
|
||||||
4:
|
|
||||||
// Check for realtime signals, thread/proc.
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// Load the pending set again. TODO: optimize this?
|
|
||||||
// Process pending - realtime
|
|
||||||
add x1, x6, #{pctl_pending}
|
|
||||||
ldaxr x2, [x1]
|
|
||||||
lsr x2, x2, #32
|
|
||||||
|
|
||||||
// Thread pending - realtime and allowset
|
|
||||||
add x5, x0, #{tcb_sc_off} + {sc_word} + 8
|
|
||||||
ldar x1, [x5]
|
|
||||||
|
|
||||||
orr x2, x1, x2 // combine proc and thread pending
|
|
||||||
and x2, x2, x2, lsr #32 // AND pending with allowset
|
|
||||||
cbz x2, 7f // spurious signal if realtime is clear
|
|
||||||
|
|
||||||
rbit x2, x2
|
|
||||||
clz x2, x2
|
|
||||||
// x2 now contains sig_idx - 32
|
|
||||||
|
|
||||||
// If realtime signal was directed at thread, handle it as an idempotent signal.
|
|
||||||
lsr x3, x1, x2 // x3 := x1 >> x2; x1 is thread pending
|
|
||||||
tbnz x3, #0, 5f // jump if bit is nonzero
|
|
||||||
|
|
||||||
// SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base | (flags << 8))
|
|
||||||
// x8 x0 x1 x2 x3 x4
|
|
||||||
|
|
||||||
mov x5, x0 // save TCB pointer
|
|
||||||
mov x6, x2
|
|
||||||
ldr x8, ={SYS_CALL}
|
|
||||||
adrp x0, {proc_fd}
|
|
||||||
ldr x0, [x0, #:lo12:{proc_fd}]
|
|
||||||
add x1, x5, #{tcb_sa_off} + {sa_tmp_rt_inf}
|
|
||||||
str x6, [x1]
|
|
||||||
mov x2, #{RTINF_SIZE}
|
|
||||||
adrp x4, {proc_call}
|
|
||||||
add x4, x4, :lo12:{proc_call}
|
|
||||||
mov x3, #1
|
|
||||||
svc 0
|
|
||||||
mov x3, x0
|
|
||||||
mov x0, x5 // restore TCB pointer
|
|
||||||
mov x2, x6 // restore signal number - 32
|
|
||||||
add x1, x2, #32 // signal number
|
|
||||||
cbnz x3, 1b
|
|
||||||
b 2f
|
|
||||||
5:
|
|
||||||
// A realtime signal was sent to this thread, try clearing its bit.
|
|
||||||
// x3 contains last rt signal word, x2 contains rt_idx
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// Calculate the absolute sig_idx
|
|
||||||
add x1, x3, 32
|
|
||||||
|
|
||||||
// Load si_pid and si_uid
|
|
||||||
add x2, x0, #{tcb_sc_off} + {sc_sender_infos}
|
|
||||||
add x2, x2, w1, uxtb #3
|
|
||||||
ldar x2, [x2]
|
|
||||||
|
|
||||||
add x3, x0, #{tcb_sc_off} + {sc_word} + 8
|
|
||||||
ldxr x2, [x3]
|
|
||||||
|
|
||||||
// Calculate new mask
|
|
||||||
mov x4, #1
|
|
||||||
lsl x4, x4, x2
|
|
||||||
sub x2, x2, x4 // remove bit
|
|
||||||
|
|
||||||
stxr w5, x2, [x3]
|
|
||||||
cbnz w5, 1b
|
|
||||||
str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}]
|
|
||||||
b 2f
|
|
||||||
3:
|
|
||||||
// A standard signal was sent to this thread, try clearing its bit.
|
|
||||||
clz w1, w1
|
|
||||||
mov x2, #31
|
|
||||||
sub x1, x2, x1
|
|
||||||
|
|
||||||
// Load si_pid and si_uid
|
|
||||||
add x2, x0, #{tcb_sc_off} + {sc_sender_infos}
|
|
||||||
add x2, x2, w1, uxtb #3
|
|
||||||
ldar x2, [x2]
|
|
||||||
|
|
||||||
// Clear bit from mask
|
|
||||||
mov x3, #1
|
|
||||||
lsl x3, x3, x1
|
|
||||||
sub x4, x4, x3
|
|
||||||
|
|
||||||
// Try updating the mask
|
|
||||||
stxr w3, x1, [x5]
|
|
||||||
cbnz w3, 1b
|
|
||||||
|
|
||||||
str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}]
|
|
||||||
2:
|
|
||||||
ldr x3, [x0, #{tcb_sa_off} + {sa_pctl}]
|
|
||||||
add x3, x3, {pctl_actions}
|
|
||||||
add x2, x3, w1, uxtb #4 // actions_base + sig_idx * sizeof Action
|
|
||||||
// TODO: NOT ATOMIC (tearing allowed between regs)!
|
|
||||||
ldxp x2, x3, [x2]
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// Calculate new sp wrt redzone and alignment
|
|
||||||
mov x4, sp
|
|
||||||
sub x4, x4, {REDZONE_SIZE}
|
|
||||||
and x4, x4, -{STACK_ALIGN}
|
|
||||||
mov sp, x4
|
|
||||||
|
|
||||||
// skip sigaltstack step if SA_ONSTACK is clear
|
|
||||||
// tbz x2, #{SA_ONSTACK_BIT}, 2f
|
|
||||||
|
|
||||||
ldr x2, [x0, #{tcb_sc_off} + {sc_saved_pc}]
|
|
||||||
ldr x3, [x0, #{tcb_sc_off} + {sc_saved_x0}]
|
|
||||||
stp x2, x3, [sp, #-16]!
|
|
||||||
|
|
||||||
ldr x2, [x0, #{tcb_sa_off} + {sa_tmp_sp}]
|
|
||||||
mrs x3, nzcv
|
|
||||||
stp x2, x3, [sp, #-16]!
|
|
||||||
|
|
||||||
ldp x2, x3, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}]
|
|
||||||
stp x2, x3, [sp, #-16]!
|
|
||||||
ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}]
|
|
||||||
stp x4, x3, [sp, #-16]!
|
|
||||||
ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}]
|
|
||||||
stp x6, x5, [sp, #-16]!
|
|
||||||
ldp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}]
|
|
||||||
stp x8, x7, [sp, #-16]!
|
|
||||||
|
|
||||||
stp x10, x9, [sp, #-16]!
|
|
||||||
stp x12, x11, [sp, #-16]!
|
|
||||||
stp x14, x13, [sp, #-16]!
|
|
||||||
stp x16, x15, [sp, #-16]!
|
|
||||||
stp x18, x17, [sp, #-16]!
|
|
||||||
stp x20, x19, [sp, #-16]!
|
|
||||||
stp x22, x21, [sp, #-16]!
|
|
||||||
stp x24, x23, [sp, #-16]!
|
|
||||||
stp x26, x25, [sp, #-16]!
|
|
||||||
stp x28, x27, [sp, #-16]!
|
|
||||||
stp x30, x29, [sp, #-16]!
|
|
||||||
|
|
||||||
str w1, [sp, #-4]
|
|
||||||
sub sp, sp, #64
|
|
||||||
|
|
||||||
mov x0, sp
|
|
||||||
bl {inner}
|
|
||||||
|
|
||||||
add sp, sp, #64
|
|
||||||
|
|
||||||
ldp x30, x29, [sp], #16
|
|
||||||
ldp x28, x27, [sp], #16
|
|
||||||
ldp x26, x25, [sp], #16
|
|
||||||
ldp x24, x23, [sp], #16
|
|
||||||
ldp x22, x21, [sp], #16
|
|
||||||
ldp x20, x19, [sp], #16
|
|
||||||
ldp x18, x17, [sp], #16
|
|
||||||
ldp x16, x15, [sp], #16
|
|
||||||
ldp x14, x13, [sp], #16
|
|
||||||
ldp x12, x11, [sp], #16
|
|
||||||
ldp x10, x9, [sp], #16
|
|
||||||
ldp x8, x7, [sp], #16
|
|
||||||
ldp x6, x5, [sp], #16
|
|
||||||
ldp x4, x3, [sp], #16
|
|
||||||
ldp x2, x1, [sp], #16
|
|
||||||
|
|
||||||
ldr x0, [sp, #8]
|
|
||||||
msr nzcv, x0
|
|
||||||
|
|
||||||
8:
|
|
||||||
// x18 is reserved by ABI as 'platform register', so clobbering it should be safe.
|
|
||||||
mov x18, sp
|
|
||||||
ldr x0, [x18]
|
|
||||||
mov sp, x0
|
|
||||||
|
|
||||||
ldp x18, x0, [x18, #16]
|
|
||||||
br x18
|
|
||||||
7:
|
|
||||||
// Spurious signal, i.e. all bitsets were 0 at the time they were checked
|
|
||||||
clrex
|
|
||||||
|
|
||||||
ldr x1, [x0, #{tcb_sc_off} + {sc_flags}]
|
|
||||||
and x1, x1, ~1
|
|
||||||
str x1, [x0, #{tcb_sc_off} + {sc_flags}]
|
|
||||||
|
|
||||||
ldp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}]
|
|
||||||
ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}]
|
|
||||||
ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}]
|
|
||||||
ldr x18, [x0, #{tcb_sc_off} + {sc_saved_pc}]
|
|
||||||
ldr x0, [x0, #{tcb_sc_off} + {sc_saved_x0}]
|
|
||||||
br x18
|
|
||||||
"] <= [
|
|
||||||
pctl_pending = const (offset_of!(SigProcControl, pending)),
|
|
||||||
pctl_actions = const (offset_of!(SigProcControl, actions)),
|
|
||||||
pctl_sender_infos = const (offset_of!(SigProcControl, sender_infos)),
|
|
||||||
tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)),
|
|
||||||
tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)),
|
|
||||||
sa_tmp_x1_x2 = const offset_of!(SigArea, tmp_x1_x2),
|
|
||||||
sa_tmp_x3_x4 = const offset_of!(SigArea, tmp_x3_x4),
|
|
||||||
sa_tmp_x5_x6 = const offset_of!(SigArea, tmp_x5_x6),
|
|
||||||
sa_tmp_x7_x8 = const offset_of!(SigArea, tmp_x7_x8),
|
|
||||||
sa_tmp_sp = const offset_of!(SigArea, tmp_sp),
|
|
||||||
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
||||||
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
||||||
sa_pctl = const offset_of!(SigArea, pctl),
|
|
||||||
sc_saved_pc = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
sc_saved_x0 = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
||||||
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
||||||
sc_word = const offset_of!(Sigcontrol, word),
|
|
||||||
sc_flags = const offset_of!(Sigcontrol, control_flags),
|
|
||||||
proc_fd = sym PROC_FD,
|
|
||||||
inner = sym inner_c,
|
|
||||||
proc_call = sym PROC_CALL,
|
|
||||||
|
|
||||||
SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000
|
|
||||||
|
|
||||||
SYS_CALL = const syscall::SYS_CALL,
|
|
||||||
|
|
||||||
STACK_ALIGN = const 16,
|
|
||||||
REDZONE_SIZE = const 128,
|
|
||||||
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_rlct_clone_ret: ["
|
|
||||||
# Load registers
|
|
||||||
ldp x8, x0, [sp], #16
|
|
||||||
ldp x1, x2, [sp], #16
|
|
||||||
ldp x3, x4, [sp], #16
|
|
||||||
|
|
||||||
# Call entry point
|
|
||||||
blr x8
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= []);
|
|
||||||
|
|
||||||
pub fn current_sp() -> usize {
|
|
||||||
let sp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mov {}, sp", out(reg) sp);
|
|
||||||
}
|
|
||||||
sp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn manually_enter_trampoline() {
|
|
||||||
let ctl = unsafe { &Tcb::current().unwrap().os_specific.control };
|
|
||||||
|
|
||||||
ctl.saved_archdep_reg.set(0);
|
|
||||||
let ip_location = &ctl.saved_ip as *const _ as usize;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("
|
|
||||||
bl 2f
|
|
||||||
b 3f
|
|
||||||
2:
|
|
||||||
str lr, [x0]
|
|
||||||
b __relibc_internal_sigentry
|
|
||||||
3:
|
|
||||||
",
|
|
||||||
inout("x0") ip_location => _, out("lr") _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn arch_pre(stack: &mut SigStack, os: &mut SigArea) -> PosixStackt {
|
|
||||||
PosixStackt {
|
|
||||||
sp: core::ptr::null_mut(), // TODO
|
|
||||||
size: 0, // TODO
|
|
||||||
flags: 0, // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
||||||
let orig_pc = core::mem::replace(
|
|
||||||
&mut stack.regs.pc,
|
|
||||||
__relibc_internal_sigentry as *const () as usize,
|
|
||||||
);
|
|
||||||
control.saved_ip.set(orig_pc);
|
|
||||||
control.saved_archdep_reg.set(stack.regs.x0);
|
|
||||||
}
|
|
||||||
pub(crate) static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
||||||
static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize];
|
|
||||||
@@ -1,418 +0,0 @@
|
|||||||
use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull, sync::atomic::Ordering};
|
|
||||||
|
|
||||||
use syscall::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
||||||
signal::{PROC_CONTROL_STRUCT, PosixStackt, RtSigarea, SigStack, inner_fastcall},
|
|
||||||
};
|
|
||||||
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
||||||
|
|
||||||
use super::ForkScratchpad;
|
|
||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
||||||
pub const STACK_TOP: usize = 1 << 31;
|
|
||||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SigArea {
|
|
||||||
pub altstack_top: usize,
|
|
||||||
pub altstack_bottom: usize,
|
|
||||||
pub tmp_eip: usize,
|
|
||||||
pub tmp_esp: usize,
|
|
||||||
pub tmp_eax: usize,
|
|
||||||
pub tmp_ebx: usize,
|
|
||||||
pub tmp_ecx: usize,
|
|
||||||
pub tmp_edx: usize,
|
|
||||||
pub tmp_edi: usize,
|
|
||||||
pub tmp_esi: usize,
|
|
||||||
pub tmp_rt_inf: RtSigInfo,
|
|
||||||
pub tmp_signo: usize,
|
|
||||||
pub tmp_id_inf: u64,
|
|
||||||
pub tmp_mm0: u64,
|
|
||||||
pub disable_signals_depth: u64,
|
|
||||||
pub last_sig_was_restart: bool,
|
|
||||||
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C, align(16))]
|
|
||||||
pub struct ArchIntRegs {
|
|
||||||
pub fxsave: [u16; 29],
|
|
||||||
|
|
||||||
// ensure fxsave region is 16 byte aligned
|
|
||||||
pub _pad: [usize; 2], // fxsave "available" +0
|
|
||||||
|
|
||||||
pub ebp: usize, // fxsave "available" +8
|
|
||||||
pub esi: usize, // avail +12
|
|
||||||
pub edi: usize, // avail +16
|
|
||||||
pub ebx: usize, // avail +20
|
|
||||||
pub eax: usize, // avail +24
|
|
||||||
pub ecx: usize, // avail +28
|
|
||||||
pub edx: usize, // avail +32
|
|
||||||
|
|
||||||
pub eflags: usize, // avail +36
|
|
||||||
pub eip: usize, // avail +40
|
|
||||||
pub esp: usize, // avail +44
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
||||||
/// is already initialized as if it was a thread.
|
|
||||||
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file = open_via_dup.dup_into_upper(b"regs/env")?;
|
|
||||||
|
|
||||||
env.fsbase = 0;
|
|
||||||
env.gsbase = 0;
|
|
||||||
|
|
||||||
file.write(&mut env)?;
|
|
||||||
crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "fastcall" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
||||||
Error::mux(fork_inner(initial_rsp, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: duplicate code with x86_64
|
|
||||||
unsafe extern "cdecl" fn child_hook(scratchpad: ForkScratchpad) {
|
|
||||||
let _ = crate::sys::close(scratchpad.cur_filetable_fd);
|
|
||||||
unsafe {
|
|
||||||
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
||||||
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
||||||
},
|
|
||||||
new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd)
|
|
||||||
.to_upper()
|
|
||||||
.unwrap(),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
||||||
mov ecx, [esp+4]
|
|
||||||
|
|
||||||
push ebp
|
|
||||||
mov ebp, esp
|
|
||||||
|
|
||||||
// Push preserved registers
|
|
||||||
push ebx
|
|
||||||
push edi
|
|
||||||
push esi
|
|
||||||
push ebp
|
|
||||||
|
|
||||||
sub esp, 32
|
|
||||||
|
|
||||||
//TODO stmxcsr [esp+16]
|
|
||||||
fnstcw [esp+24]
|
|
||||||
|
|
||||||
mov edx, esp
|
|
||||||
call {fork_impl}
|
|
||||||
|
|
||||||
jmp 2f
|
|
||||||
"] <= [fork_impl = sym fork_impl]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_ret: ["
|
|
||||||
// Arguments already on the stack
|
|
||||||
call {child_hook}
|
|
||||||
|
|
||||||
//TODO ldmxcsr [esp+16]
|
|
||||||
fldcw [esp+24]
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
|
|
||||||
.p2align 4
|
|
||||||
2:
|
|
||||||
add esp, 32
|
|
||||||
|
|
||||||
// Pop preserved registers
|
|
||||||
pop ebp
|
|
||||||
pop esi
|
|
||||||
pop edi
|
|
||||||
pop ebx
|
|
||||||
|
|
||||||
pop ebp
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= [child_hook = sym child_hook]);
|
|
||||||
asmfunction!(__relibc_internal_sigentry: ["
|
|
||||||
// Save some registers
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_esp}], esp
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_eax}], eax
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_edx}], edx
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_ecx}], ecx
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_ebx}], ebx
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_edi}], edi
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_esi}], esi
|
|
||||||
1:
|
|
||||||
// Read standard signal word - first for this thread
|
|
||||||
mov edx, gs:[{tcb_sc_off} + {sc_word} + 4]
|
|
||||||
mov eax, gs:[{tcb_sc_off} + {sc_word}]
|
|
||||||
and eax, edx
|
|
||||||
bsf eax, eax
|
|
||||||
jnz 9f
|
|
||||||
|
|
||||||
// Read standard signal word - for the process
|
|
||||||
lea ecx, [{pctl}]
|
|
||||||
mov eax, [ecx + {pctl_pending}]
|
|
||||||
and eax, edx
|
|
||||||
bsf eax, eax
|
|
||||||
jz 3f
|
|
||||||
|
|
||||||
// Read si_pid and si_uid, atomically.
|
|
||||||
movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0
|
|
||||||
movq mm0, [ecx + {pctl_sender_infos} + eax * 8]
|
|
||||||
movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0
|
|
||||||
movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}]
|
|
||||||
|
|
||||||
// Try clearing the pending bit, otherwise retry if another thread did that first
|
|
||||||
lock btr [ecx + {pctl_pending}], eax
|
|
||||||
jnc 1b
|
|
||||||
jmp 2f
|
|
||||||
3:
|
|
||||||
// Read realtime thread and process signal word together
|
|
||||||
mov edx, [ecx + {pctl_pending} + 4]
|
|
||||||
mov eax, gs:[{tcb_sc_off} + {sc_word} + 8]
|
|
||||||
or eax, edx
|
|
||||||
and eax, gs:[{tcb_sc_off} + {sc_word} + 12]
|
|
||||||
bsf eax, eax
|
|
||||||
jz 7f // spurious signal
|
|
||||||
|
|
||||||
// If thread rather than process was specifically targeted, send the signal to it first.
|
|
||||||
bt edx, eax
|
|
||||||
jnc 8f
|
|
||||||
|
|
||||||
// SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base)
|
|
||||||
// eax ebx ecx edx esi edi
|
|
||||||
|
|
||||||
mov ebx, [{proc_fd}]
|
|
||||||
mov ecx, gs:[0]
|
|
||||||
add ecx, {tcb_sa_off} + {sa_tmp_rt_inf}
|
|
||||||
mov [ecx], eax
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_signo}], eax
|
|
||||||
mov edx, {RTINF_SIZE}
|
|
||||||
mov esi, 1
|
|
||||||
lea edi, [{proc_call}]
|
|
||||||
mov eax, {SYS_CALL}
|
|
||||||
int 0x80
|
|
||||||
test eax, eax
|
|
||||||
jnz 1b
|
|
||||||
|
|
||||||
mov eax, gs:[{tcb_sa_off} + {sa_tmp_signo}]
|
|
||||||
add eax, 32
|
|
||||||
jmp 2f
|
|
||||||
8:
|
|
||||||
add eax, 32
|
|
||||||
9:
|
|
||||||
// Read si_pid and si_uid, atomically.
|
|
||||||
movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0
|
|
||||||
movq mm0, gs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8]
|
|
||||||
movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0
|
|
||||||
movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}]
|
|
||||||
mov edx, eax
|
|
||||||
shr edx, 5
|
|
||||||
mov ecx, eax
|
|
||||||
and ecx, 31
|
|
||||||
lock btr gs:[{tcb_sc_off} + {sc_word} + edx * 8], ecx
|
|
||||||
|
|
||||||
add eax, 64
|
|
||||||
2:
|
|
||||||
and esp, -{STACK_ALIGN}
|
|
||||||
|
|
||||||
mov edx, eax
|
|
||||||
add edx, edx
|
|
||||||
bt dword ptr [{pctl} + {pctl_actions} + edx * 8 + 4], 28
|
|
||||||
jnc 4f
|
|
||||||
|
|
||||||
mov edx, gs:[{tcb_sa_off} + {sa_altstack_top}]
|
|
||||||
cmp esp, edx
|
|
||||||
ja 3f
|
|
||||||
|
|
||||||
cmp esp, gs:[{tcb_sa_off} + {sa_altstack_bottom}]
|
|
||||||
jnbe 4f
|
|
||||||
3:
|
|
||||||
mov esp, edx
|
|
||||||
4:
|
|
||||||
// Now that we have a stack, we can finally start populating the signal stack.
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esp}]
|
|
||||||
push dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
||||||
push dword ptr gs:[{tcb_sc_off} + {sc_saved_eflags}]
|
|
||||||
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edx}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ecx}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_eax}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ebx}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edi}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esi}]
|
|
||||||
push ebp
|
|
||||||
|
|
||||||
sub esp, 2 * 4 + 29 * 16
|
|
||||||
fxsave [esp]
|
|
||||||
|
|
||||||
mov [esp - 4], eax
|
|
||||||
sub esp, 48
|
|
||||||
|
|
||||||
mov ecx, esp
|
|
||||||
call {inner}
|
|
||||||
|
|
||||||
fxrstor [esp + 48]
|
|
||||||
add esp, 48 + 29 * 16 + 2 * 4
|
|
||||||
|
|
||||||
pop ebp
|
|
||||||
pop esi
|
|
||||||
pop edi
|
|
||||||
pop ebx
|
|
||||||
pop eax
|
|
||||||
pop ecx
|
|
||||||
pop edx
|
|
||||||
|
|
||||||
popfd
|
|
||||||
pop dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_first
|
|
||||||
__relibc_internal_sigentry_crit_first:
|
|
||||||
pop esp
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_second
|
|
||||||
__relibc_internal_sigentry_crit_second:
|
|
||||||
jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
||||||
7:
|
|
||||||
mov eax, gs:[0]
|
|
||||||
lea esp, [eax + {tcb_sc_off} + {sc_saved_eflags}]
|
|
||||||
popfd
|
|
||||||
|
|
||||||
mov esp, gs:[{tcb_sa_off} + {sa_tmp_esp}]
|
|
||||||
|
|
||||||
mov eax, gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_eip}], eax
|
|
||||||
|
|
||||||
mov eax, gs:[{tcb_sa_off} + {sa_tmp_eax}]
|
|
||||||
mov ebx, gs:[{tcb_sa_off} + {sa_tmp_ebx}]
|
|
||||||
mov ecx, gs:[{tcb_sa_off} + {sa_tmp_ecx}]
|
|
||||||
mov edx, gs:[{tcb_sa_off} + {sa_tmp_edx}]
|
|
||||||
mov edi, gs:[{tcb_sa_off} + {sa_tmp_edi}]
|
|
||||||
mov esi, gs:[{tcb_sa_off} + {sa_tmp_esi}]
|
|
||||||
|
|
||||||
and dword ptr gs:[{tcb_sc_off} + {sc_control}], ~1
|
|
||||||
.globl __relibc_internal_sigentry_crit_third
|
|
||||||
__relibc_internal_sigentry_crit_third:
|
|
||||||
jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
||||||
"] <= [
|
|
||||||
inner = sym inner_fastcall,
|
|
||||||
sa_tmp_eip = const offset_of!(SigArea, tmp_eip),
|
|
||||||
sa_tmp_esp = const offset_of!(SigArea, tmp_esp),
|
|
||||||
sa_tmp_eax = const offset_of!(SigArea, tmp_eax),
|
|
||||||
sa_tmp_ebx = const offset_of!(SigArea, tmp_ebx),
|
|
||||||
sa_tmp_ecx = const offset_of!(SigArea, tmp_ecx),
|
|
||||||
sa_tmp_edx = const offset_of!(SigArea, tmp_edx),
|
|
||||||
sa_tmp_edi = const offset_of!(SigArea, tmp_edi),
|
|
||||||
sa_tmp_esi = const offset_of!(SigArea, tmp_esi),
|
|
||||||
sa_tmp_mm0 = const offset_of!(SigArea, tmp_mm0),
|
|
||||||
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
||||||
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
||||||
sa_tmp_signo = const offset_of!(SigArea, tmp_signo),
|
|
||||||
sa_altstack_top = const offset_of!(SigArea, altstack_top),
|
|
||||||
sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom),
|
|
||||||
sc_control = const offset_of!(Sigcontrol, control_flags),
|
|
||||||
sc_saved_eflags = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
||||||
sc_saved_eip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
sc_word = const offset_of!(Sigcontrol, word),
|
|
||||||
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
||||||
tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch),
|
|
||||||
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
||||||
pctl_actions = const offset_of!(SigProcControl, actions),
|
|
||||||
pctl_sender_infos = const offset_of!(SigProcControl, sender_infos),
|
|
||||||
pctl_pending = const offset_of!(SigProcControl, pending),
|
|
||||||
pctl = sym PROC_CONTROL_STRUCT,
|
|
||||||
proc_fd = sym PROC_FD,
|
|
||||||
proc_call = sym PROC_CALL,
|
|
||||||
STACK_ALIGN = const 16,
|
|
||||||
SYS_CALL = const syscall::SYS_CALL,
|
|
||||||
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_rlct_clone_ret -> usize: ["
|
|
||||||
# Load registers
|
|
||||||
pop eax
|
|
||||||
|
|
||||||
sub esp, 8
|
|
||||||
|
|
||||||
mov DWORD PTR [esp], 0x00001F80
|
|
||||||
# TODO: ldmxcsr [esp]
|
|
||||||
mov WORD PTR [esp], 0x037F
|
|
||||||
fldcw [esp]
|
|
||||||
|
|
||||||
add esp, 8
|
|
||||||
|
|
||||||
# Call entry point
|
|
||||||
call eax
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= []);
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn __relibc_internal_sigentry_crit_first();
|
|
||||||
fn __relibc_internal_sigentry_crit_second();
|
|
||||||
fn __relibc_internal_sigentry_crit_third();
|
|
||||||
}
|
|
||||||
pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt {
|
|
||||||
if stack.regs.eip == __relibc_internal_sigentry_crit_first as *const () as usize {
|
|
||||||
let stack_ptr = stack.regs.esp as *const usize;
|
|
||||||
stack.regs.esp = unsafe { stack_ptr.read() };
|
|
||||||
stack.regs.eip = unsafe { stack_ptr.sub(1).read() };
|
|
||||||
} else if stack.regs.eip == __relibc_internal_sigentry_crit_second as *const () as usize
|
|
||||||
|| stack.regs.eip == __relibc_internal_sigentry_crit_third as *const () as usize
|
|
||||||
{
|
|
||||||
stack.regs.eip = area.tmp_eip;
|
|
||||||
}
|
|
||||||
PosixStackt {
|
|
||||||
sp: stack.regs.esp as *mut (),
|
|
||||||
size: 0, // TODO
|
|
||||||
flags: 0, // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
||||||
let orig_eip = core::mem::replace(
|
|
||||||
&mut stack.regs.eip,
|
|
||||||
__relibc_internal_sigentry as *const () as usize,
|
|
||||||
);
|
|
||||||
control.saved_ip.set(orig_eip);
|
|
||||||
control.saved_archdep_reg.set(stack.regs.eflags);
|
|
||||||
}
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe fn manually_enter_trampoline() {
|
|
||||||
let c = unsafe { &crate::Tcb::current().unwrap().os_specific.control };
|
|
||||||
c.control_flags.store(
|
|
||||||
c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(),
|
|
||||||
Ordering::Release,
|
|
||||||
);
|
|
||||||
c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86?
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("
|
|
||||||
call 2f
|
|
||||||
jmp 3f
|
|
||||||
2:
|
|
||||||
pop dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
||||||
jmp __relibc_internal_sigentry
|
|
||||||
3:
|
|
||||||
",
|
|
||||||
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
||||||
sc_saved_eip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get current stack pointer, weak granularity guarantees.
|
|
||||||
pub fn current_sp() -> usize {
|
|
||||||
let sp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mov {}, esp", out(reg) sp);
|
|
||||||
}
|
|
||||||
sp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
||||||
static PROC_CALL: u64 = ProcCall::Sigdeq as u64;
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
pub use self::aarch64::*;
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
pub mod aarch64;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub use self::i686::*;
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub mod i686;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub use self::x86_64::*;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub mod x86_64;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
pub use self::riscv64::*;
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
pub mod riscv64;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ForkScratchpad {
|
|
||||||
pub cur_filetable_fd: usize,
|
|
||||||
pub new_filetable_fd: usize,
|
|
||||||
pub new_proc_fd: usize,
|
|
||||||
pub new_thr_fd: usize,
|
|
||||||
}
|
|
||||||
@@ -1,661 +0,0 @@
|
|||||||
use core::cell::SyncUnsafeCell;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Tcb,
|
|
||||||
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
||||||
signal::{PosixStackt, RtSigarea, SigStack, get_sigaltstack, inner_c},
|
|
||||||
};
|
|
||||||
use core::{mem::offset_of, ptr::NonNull, sync::atomic::Ordering};
|
|
||||||
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
||||||
use syscall::{data::*, error::*};
|
|
||||||
|
|
||||||
use super::ForkScratchpad;
|
|
||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
||||||
pub const STACK_TOP: usize = 1 << 38;
|
|
||||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SigArea {
|
|
||||||
pub tmp_sp: u64,
|
|
||||||
pub tmp_t1: u64,
|
|
||||||
pub tmp_t2: u64,
|
|
||||||
pub tmp_t3: u64,
|
|
||||||
pub tmp_t4: u64,
|
|
||||||
pub tmp_a0: u64,
|
|
||||||
pub tmp_a1: u64,
|
|
||||||
pub tmp_a2: u64,
|
|
||||||
pub tmp_a3: u64,
|
|
||||||
pub tmp_a4: u64,
|
|
||||||
pub tmp_a7: u64,
|
|
||||||
|
|
||||||
pub pctl: usize, // TODO: remove
|
|
||||||
pub tmp_ip: u64,
|
|
||||||
pub tmp_rt_inf: RtSigInfo,
|
|
||||||
pub tmp_id_inf: u64,
|
|
||||||
pub altstack_top: usize,
|
|
||||||
pub altstack_bottom: usize,
|
|
||||||
pub disable_signals_depth: u64,
|
|
||||||
pub last_sig_was_restart: bool,
|
|
||||||
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct ArchIntRegs {
|
|
||||||
pub int_regs: [u64; 31],
|
|
||||||
pub pc: u64,
|
|
||||||
pub fp_regs: [u64; 32],
|
|
||||||
pub fcsr: u32,
|
|
||||||
_pad: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
||||||
/// is already initialized as if it was a thread.
|
|
||||||
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file = open_via_dup.dup_into_upper(b"regs/env")?;
|
|
||||||
|
|
||||||
env.tp = 0;
|
|
||||||
|
|
||||||
file.write(&mut env)?;
|
|
||||||
crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
||||||
Error::mux(fork_inner(initial_rsp, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) {
|
|
||||||
let _ = crate::sys::close(scratchpad.cur_filetable_fd);
|
|
||||||
unsafe {
|
|
||||||
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
||||||
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
||||||
},
|
|
||||||
new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd)
|
|
||||||
.to_upper()
|
|
||||||
.unwrap(),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
||||||
.attribute arch, \"rv64gc\" # rust bug 80608
|
|
||||||
addi sp, sp, -200
|
|
||||||
sd s0, 0(sp)
|
|
||||||
sd s1, 8(sp)
|
|
||||||
sd s2, 16(sp)
|
|
||||||
sd s3, 24(sp)
|
|
||||||
sd s4, 32(sp)
|
|
||||||
sd s5, 40(sp)
|
|
||||||
sd s6, 48(sp)
|
|
||||||
sd s7, 56(sp)
|
|
||||||
sd s8, 64(sp)
|
|
||||||
sd s9, 72(sp)
|
|
||||||
sd s10, 80(sp)
|
|
||||||
sd s11, 88(sp)
|
|
||||||
sd ra, 96(sp)
|
|
||||||
|
|
||||||
fsd fs0, 104(sp)
|
|
||||||
fsd fs1, 112(sp)
|
|
||||||
fsd fs2, 120(sp)
|
|
||||||
fsd fs3, 128(sp)
|
|
||||||
fsd fs4, 136(sp)
|
|
||||||
fsd fs5, 144(sp)
|
|
||||||
fsd fs6, 152(sp)
|
|
||||||
fsd fs7, 160(sp)
|
|
||||||
fsd fs8, 168(sp)
|
|
||||||
fsd fs9, 176(sp)
|
|
||||||
fsd fs10, 184(sp)
|
|
||||||
fsd fs11, 192(sp)
|
|
||||||
|
|
||||||
// a0 is forwarded from this function
|
|
||||||
mv a1, sp
|
|
||||||
jal {fork_impl}
|
|
||||||
|
|
||||||
ld s0, 0(sp)
|
|
||||||
ld s1, 8(sp)
|
|
||||||
ld s2, 16(sp)
|
|
||||||
ld s3, 24(sp)
|
|
||||||
ld s4, 32(sp)
|
|
||||||
ld s5, 40(sp)
|
|
||||||
ld s6, 48(sp)
|
|
||||||
ld s7, 56(sp)
|
|
||||||
ld s8, 64(sp)
|
|
||||||
ld s9, 72(sp)
|
|
||||||
ld s10, 80(sp)
|
|
||||||
ld s11, 88(sp)
|
|
||||||
ld ra, 96(sp)
|
|
||||||
|
|
||||||
fld fs0, 104(sp)
|
|
||||||
fld fs1, 112(sp)
|
|
||||||
fld fs2, 120(sp)
|
|
||||||
fld fs3, 128(sp)
|
|
||||||
fld fs4, 136(sp)
|
|
||||||
fld fs5, 144(sp)
|
|
||||||
fld fs6, 152(sp)
|
|
||||||
fld fs7, 160(sp)
|
|
||||||
fld fs8, 168(sp)
|
|
||||||
fld fs9, 176(sp)
|
|
||||||
fld fs10, 184(sp)
|
|
||||||
fld fs11, 192(sp)
|
|
||||||
|
|
||||||
addi sp, sp, 200
|
|
||||||
ret
|
|
||||||
"] <= [fork_impl = sym fork_impl]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_ret: ["
|
|
||||||
.attribute arch, \"rv64gc\" # rust bug 80608
|
|
||||||
|
|
||||||
# scratchpad is in a1, move to a0 for child_hook
|
|
||||||
mv a0, a1
|
|
||||||
|
|
||||||
jal {child_hook}
|
|
||||||
|
|
||||||
mv a0, zero
|
|
||||||
|
|
||||||
ld s0, 0(sp)
|
|
||||||
ld s1, 8(sp)
|
|
||||||
ld s2, 16(sp)
|
|
||||||
ld s3, 24(sp)
|
|
||||||
ld s4, 32(sp)
|
|
||||||
ld s5, 40(sp)
|
|
||||||
ld s6, 48(sp)
|
|
||||||
ld s7, 56(sp)
|
|
||||||
ld s8, 64(sp)
|
|
||||||
ld s9, 72(sp)
|
|
||||||
ld s10, 80(sp)
|
|
||||||
ld s11, 88(sp)
|
|
||||||
ld ra, 96(sp)
|
|
||||||
|
|
||||||
fld fs0, 104(sp)
|
|
||||||
fld fs1, 112(sp)
|
|
||||||
fld fs2, 120(sp)
|
|
||||||
fld fs3, 128(sp)
|
|
||||||
fld fs4, 136(sp)
|
|
||||||
fld fs5, 144(sp)
|
|
||||||
fld fs6, 152(sp)
|
|
||||||
fld fs7, 160(sp)
|
|
||||||
fld fs8, 168(sp)
|
|
||||||
fld fs9, 176(sp)
|
|
||||||
fld fs10, 184(sp)
|
|
||||||
fld fs11, 192(sp)
|
|
||||||
|
|
||||||
addi sp, sp, 200
|
|
||||||
ret
|
|
||||||
"] <= [child_hook = sym child_hook]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_sigentry: ["
|
|
||||||
.attribute arch, \"rv64gc\" # rust bug 80608
|
|
||||||
// Save some registers
|
|
||||||
ld t0, -8(tp) // Tcb
|
|
||||||
sd sp, ({tcb_sa_off} + {sa_tmp_sp})(t0)
|
|
||||||
sd t1, ({tcb_sa_off} + {sa_tmp_t1})(t0)
|
|
||||||
sd t2, ({tcb_sa_off} + {sa_tmp_t2})(t0)
|
|
||||||
sd t3, ({tcb_sa_off} + {sa_tmp_t3})(t0)
|
|
||||||
sd t4, ({tcb_sa_off} + {sa_tmp_t4})(t0)
|
|
||||||
ld t4, ({tcb_sa_off} + {sa_off_pctl})(t0)
|
|
||||||
|
|
||||||
// First, select signal, always pick first available bit
|
|
||||||
99:
|
|
||||||
// Read first signal word
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_word})(t0)
|
|
||||||
srli t2, t1, 32 // bitset to low word
|
|
||||||
and t1, t1, t2 // masked bitset in low word
|
|
||||||
beqz t1, 3f
|
|
||||||
|
|
||||||
// Found in first thread signal word
|
|
||||||
mv t3, x0
|
|
||||||
2: andi t2, t1, 1
|
|
||||||
bnez t2, 10f
|
|
||||||
addi t3, t3, 1
|
|
||||||
srli t1, t1, 1
|
|
||||||
j 2b
|
|
||||||
|
|
||||||
// If no unblocked thread signal was found, check for process.
|
|
||||||
// This is competitive; we need to atomically check if *we* cleared the process-wide pending
|
|
||||||
// bit, otherwise restart.
|
|
||||||
3: lw t1, {pctl_off_pending}(t4)
|
|
||||||
and t1, t1, t2
|
|
||||||
beqz t1, 3f
|
|
||||||
// Found in first process signal word
|
|
||||||
li t3, -1
|
|
||||||
2: andi t2, t1, 1
|
|
||||||
addi t3, t3, 1
|
|
||||||
srli t1, t1, 1
|
|
||||||
beqz t2, 2b
|
|
||||||
slli t1, t3, 3 // * 8 == size_of SenderInfo
|
|
||||||
add t1, t1, t4
|
|
||||||
ld t1, {pctl_off_sender_infos}(t1)
|
|
||||||
sd t1, ({tcb_sa_off} + {sa_tmp_id_inf})(t0)
|
|
||||||
li t1, 1
|
|
||||||
sll t1, t1, t3
|
|
||||||
not t1, t1
|
|
||||||
addi t2, t4, {pctl_off_pending}
|
|
||||||
amoand.w.aq t2, t1, (t2)
|
|
||||||
and t1, t1, t2
|
|
||||||
bne t1, t2, 9f
|
|
||||||
|
|
||||||
3:
|
|
||||||
// Read second signal word - both process and thread simultaneously.
|
|
||||||
// This must be done since POSIX requires low realtime signals to be picked first.
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_word} + 8)(t0)
|
|
||||||
lw t2, ({pctl_off_pending} + 4)(t4)
|
|
||||||
or t4, t1, t2
|
|
||||||
srli t2, t1, 32
|
|
||||||
and t4, t2, t4
|
|
||||||
beqz t4, 7f
|
|
||||||
li t3, -1
|
|
||||||
2: andi t2, t4, 1
|
|
||||||
addi t3, t3, 1
|
|
||||||
srli t4, t4, 1
|
|
||||||
beqz t2, 2b
|
|
||||||
li t2, 1
|
|
||||||
sll t2, t2, t3
|
|
||||||
and t1, t1, t2
|
|
||||||
addi t3, t3, 32
|
|
||||||
bnez t1, 10f // thread signal
|
|
||||||
|
|
||||||
// otherwise, try (competitively) dequeueing realtime signal
|
|
||||||
|
|
||||||
// SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base)
|
|
||||||
// a7 a0 a1 a2 a3 a4
|
|
||||||
|
|
||||||
// TODO: This SYS_CALL invocation has not yet been tested due to toolchain issues.
|
|
||||||
|
|
||||||
sd a0, ({tcb_sa_off} + {sa_tmp_a0})(t0)
|
|
||||||
sd a1, ({tcb_sa_off} + {sa_tmp_a1})(t0)
|
|
||||||
sd a2, ({tcb_sa_off} + {sa_tmp_a2})(t0)
|
|
||||||
sd a3, ({tcb_sa_off} + {sa_tmp_a3})(t0)
|
|
||||||
sd a4, ({tcb_sa_off} + {sa_tmp_a4})(t0)
|
|
||||||
sd a7, ({tcb_sa_off} + {sa_tmp_a7})(t0)
|
|
||||||
li a7, {SYS_CALL}
|
|
||||||
addi a2, t3, -32
|
|
||||||
add a1, t0, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig
|
|
||||||
sd a2, (a1)
|
|
||||||
li a2, {RTINF_SIZE}
|
|
||||||
li a3, 1
|
|
||||||
1337:
|
|
||||||
auipc a4, %pcrel_hi({proc_fd})
|
|
||||||
addi a4, a4, %pcrel_lo(1337b)
|
|
||||||
ecall
|
|
||||||
ld a3, ({tcb_sa_off} + {sa_tmp_a3})(t0)
|
|
||||||
ld a4, ({tcb_sa_off} + {sa_tmp_a4})(t0)
|
|
||||||
bnez a0, 99b // assumes error can only be EAGAIN
|
|
||||||
j 9f
|
|
||||||
|
|
||||||
10: // thread signal. t3 holds signal number
|
|
||||||
srli t1, t3, 5
|
|
||||||
bnez t1, 2f // FIXME senderinfo?
|
|
||||||
sll t2, t3, 3 // * 8 == size_of SenderInfo
|
|
||||||
add t2, t2, t0
|
|
||||||
ld t2, ({tcb_sc_off} + {sc_sender_infos})(t2)
|
|
||||||
sd t2, ({tcb_sa_off} + {sa_tmp_id_inf})(t0)
|
|
||||||
2: andi t4, t3, 31
|
|
||||||
li t2, 1
|
|
||||||
sll t2, t2, t4
|
|
||||||
not t2, t2
|
|
||||||
sll t1, t1, 3
|
|
||||||
add t1, t1, t0
|
|
||||||
addi t1, t1, {tcb_sc_off} + {sc_word}
|
|
||||||
amoand.w.aq x0, t2, (t1)
|
|
||||||
addi t3, t3, 64 // indicate signal was targeted at thread
|
|
||||||
|
|
||||||
9: // process signal t3 holds signal number
|
|
||||||
|
|
||||||
// By now we have selected a signal, stored in eax (6-bit). We now need to choose whether or
|
|
||||||
// not to switch to the alternate signal stack. If SA_ONSTACK is clear for this signal, then
|
|
||||||
// skip the sigaltstack logic.
|
|
||||||
ld t4, ({tcb_sa_off} + {sa_off_pctl})(t0)
|
|
||||||
andi t1, t3, 63
|
|
||||||
slli t1, t1, 4 // * 16 == size_of RawAction
|
|
||||||
add t1, t1, t4
|
|
||||||
ld t1, {pctl_off_actions}(t1)
|
|
||||||
slli t1, t1, 63-58 // SA_ONSTACK in sign bit
|
|
||||||
bgez t1, 3f
|
|
||||||
|
|
||||||
// If current RSP is above altstack region, switch to altstack
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_altstack_top})(t0)
|
|
||||||
bgtu sp, t1, 2f
|
|
||||||
ld t2, ({tcb_sa_off} + {sa_altstack_bottom})(t0)
|
|
||||||
bgtu sp, t3, 3f
|
|
||||||
2: mv sp, t1
|
|
||||||
3:
|
|
||||||
// form mcontext on stack
|
|
||||||
addi sp, sp, -33 * 8
|
|
||||||
fsd f0, (0 * 8)(sp)
|
|
||||||
fsd f1, (1 * 8)(sp)
|
|
||||||
fsd f2, (2 * 8)(sp)
|
|
||||||
fsd f3, (3 * 8)(sp)
|
|
||||||
fsd f4, (4 * 8)(sp)
|
|
||||||
fsd f5, (5 * 8)(sp)
|
|
||||||
fsd f6, (6 * 8)(sp)
|
|
||||||
fsd f7, (7 * 8)(sp)
|
|
||||||
fsd f8, (8 * 8)(sp)
|
|
||||||
fsd f9, (9 * 8)(sp)
|
|
||||||
fsd f10, (10 * 8)(sp)
|
|
||||||
fsd f11, (11 * 8)(sp)
|
|
||||||
fsd f12, (12 * 8)(sp)
|
|
||||||
fsd f13, (13 * 8)(sp)
|
|
||||||
fsd f14, (14 * 8)(sp)
|
|
||||||
fsd f15, (15 * 8)(sp)
|
|
||||||
fsd f16, (16 * 8)(sp)
|
|
||||||
fsd f17, (17 * 8)(sp)
|
|
||||||
fsd f18, (18 * 8)(sp)
|
|
||||||
fsd f19, (19 * 8)(sp)
|
|
||||||
fsd f20, (20 * 8)(sp)
|
|
||||||
fsd f21, (21 * 8)(sp)
|
|
||||||
fsd f22, (22 * 8)(sp)
|
|
||||||
fsd f23, (23 * 8)(sp)
|
|
||||||
fsd f24, (24 * 8)(sp)
|
|
||||||
fsd f25, (25 * 8)(sp)
|
|
||||||
fsd f26, (26 * 8)(sp)
|
|
||||||
fsd f27, (27 * 8)(sp)
|
|
||||||
fsd f28, (28 * 8)(sp)
|
|
||||||
fsd f29, (29 * 8)(sp)
|
|
||||||
fsd f30, (30 * 8)(sp)
|
|
||||||
fsd f31, (31 * 8)(sp)
|
|
||||||
csrr t1, fcsr
|
|
||||||
sw t1, (32 * 8)(sp)
|
|
||||||
|
|
||||||
addi sp, sp, -32 * 8
|
|
||||||
sd x1, 0(sp)
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_sp})(t0)
|
|
||||||
sd t1, (1 * 8)(sp) // x2 is sp
|
|
||||||
sd x3, (2 * 8)(sp)
|
|
||||||
sd x4, (3 * 8)(sp)
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_saved_t0})(t0)
|
|
||||||
sd t1, (4 * 8)(sp) // x5 is t0
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t0)
|
|
||||||
sd t1, (5 * 8)(sp) // x6 is t1
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t2})(t0)
|
|
||||||
sd t1, (6 * 8)(sp) // x7 is t2
|
|
||||||
sd x8, (7 * 8)(sp)
|
|
||||||
sd x9, (8 * 8)(sp)
|
|
||||||
sd x10, (9 * 8)(sp)
|
|
||||||
sd x11, (10 * 8)(sp)
|
|
||||||
sd x12, (11 * 8)(sp)
|
|
||||||
sd x13, (12 * 8)(sp)
|
|
||||||
sd x14, (13 * 8)(sp)
|
|
||||||
sd x15, (14 * 8)(sp)
|
|
||||||
sd x16, (15 * 8)(sp)
|
|
||||||
sd x17, (16 * 8)(sp)
|
|
||||||
sd x18, (17 * 8)(sp)
|
|
||||||
sd x19, (18 * 8)(sp)
|
|
||||||
sd x20, (19 * 8)(sp)
|
|
||||||
sd x21, (20 * 8)(sp)
|
|
||||||
sd x22, (21 * 8)(sp)
|
|
||||||
sd x23, (22 * 8)(sp)
|
|
||||||
sd x24, (23 * 8)(sp)
|
|
||||||
sd x25, (24 * 8)(sp)
|
|
||||||
sd x26, (25 * 8)(sp)
|
|
||||||
sd x27, (26 * 8)(sp)
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t3})(t0)
|
|
||||||
sd t1, (27 * 8)(sp) // t3 is x28
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t4})(t0)
|
|
||||||
sd t1, (28 * 8)(sp) // t4 is x29
|
|
||||||
sd x30, (29 * 8)(sp)
|
|
||||||
sd x31, (30 * 8)(sp)
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_saved_ip})(t0)
|
|
||||||
sd t1, (31 * 8)(sp)
|
|
||||||
|
|
||||||
// form ucontext
|
|
||||||
addi sp, sp, -64
|
|
||||||
sw t3, 60(sp)
|
|
||||||
|
|
||||||
mv t0, sp
|
|
||||||
jal {inner}
|
|
||||||
|
|
||||||
addi sp, sp, 64
|
|
||||||
|
|
||||||
addi t0, sp, 32 * 8
|
|
||||||
fld f0, (0 * 8)(t0)
|
|
||||||
fld f1, (1 * 8)(t0)
|
|
||||||
fld f2, (2 * 8)(t0)
|
|
||||||
fld f3, (3 * 8)(t0)
|
|
||||||
fld f4, (4 * 8)(t0)
|
|
||||||
fld f5, (5 * 8)(t0)
|
|
||||||
fld f6, (6 * 8)(t0)
|
|
||||||
fld f7, (7 * 8)(t0)
|
|
||||||
fld f8, (8 * 8)(t0)
|
|
||||||
fld f9, (9 * 8)(t0)
|
|
||||||
fld f10, (10 * 8)(t0)
|
|
||||||
fld f11, (11 * 8)(t0)
|
|
||||||
fld f12, (12 * 8)(t0)
|
|
||||||
fld f13, (13 * 8)(t0)
|
|
||||||
fld f14, (14 * 8)(t0)
|
|
||||||
fld f15, (15 * 8)(t0)
|
|
||||||
fld f16, (16 * 8)(t0)
|
|
||||||
fld f17, (17 * 8)(t0)
|
|
||||||
fld f18, (18 * 8)(t0)
|
|
||||||
fld f19, (19 * 8)(t0)
|
|
||||||
fld f20, (20 * 8)(t0)
|
|
||||||
fld f21, (21 * 8)(t0)
|
|
||||||
fld f22, (22 * 8)(t0)
|
|
||||||
fld f23, (23 * 8)(t0)
|
|
||||||
fld f24, (24 * 8)(t0)
|
|
||||||
fld f25, (25 * 8)(t0)
|
|
||||||
fld f26, (26 * 8)(t0)
|
|
||||||
fld f27, (27 * 8)(t0)
|
|
||||||
fld f28, (28 * 8)(t0)
|
|
||||||
fld f29, (29 * 8)(t0)
|
|
||||||
fld f30, (30 * 8)(t0)
|
|
||||||
fld f31, (31 * 8)(t0)
|
|
||||||
lw t1, (32 * 8)(t0)
|
|
||||||
csrw fcsr, t1
|
|
||||||
|
|
||||||
ld x1, 0(sp)
|
|
||||||
// skip sp
|
|
||||||
// skip gp
|
|
||||||
ld x4, (3 * 8)(sp)
|
|
||||||
ld x5, (4 * 8)(sp)
|
|
||||||
ld x6, (5 * 8)(sp)
|
|
||||||
ld x7, (6 * 8)(sp)
|
|
||||||
ld x8, (7 * 8)(sp)
|
|
||||||
ld x9, (8 * 8)(sp)
|
|
||||||
ld x10, (9 * 8)(sp)
|
|
||||||
ld x11, (10 * 8)(sp)
|
|
||||||
ld x12, (11 * 8)(sp)
|
|
||||||
ld x13, (12 * 8)(sp)
|
|
||||||
ld x14, (13 * 8)(sp)
|
|
||||||
ld x15, (14 * 8)(sp)
|
|
||||||
ld x16, (15 * 8)(sp)
|
|
||||||
ld x17, (16 * 8)(sp)
|
|
||||||
ld x18, (17 * 8)(sp)
|
|
||||||
ld x19, (18 * 8)(sp)
|
|
||||||
ld x20, (19 * 8)(sp)
|
|
||||||
ld x21, (20 * 8)(sp)
|
|
||||||
ld x22, (21 * 8)(sp)
|
|
||||||
ld x23, (22 * 8)(sp)
|
|
||||||
ld x24, (23 * 8)(sp)
|
|
||||||
ld x25, (24 * 8)(sp)
|
|
||||||
ld x26, (25 * 8)(sp)
|
|
||||||
ld x27, (26 * 8)(sp)
|
|
||||||
ld x28, (27 * 8)(sp)
|
|
||||||
ld x29, (28 * 8)(sp)
|
|
||||||
ld x30, (29 * 8)(sp)
|
|
||||||
ld x31, (30 * 8)(sp)
|
|
||||||
ld gp, (31 * 8)(sp) // new IP; this clobbers register x3/gp which is ABI reserved
|
|
||||||
.global __relibc_internal_sigentry_crit_first
|
|
||||||
__relibc_internal_sigentry_crit_first:
|
|
||||||
ld sp, (1 * 8)(sp)
|
|
||||||
.global __relibc_internal_sigentry_crit_second
|
|
||||||
__relibc_internal_sigentry_crit_second:
|
|
||||||
jr gp
|
|
||||||
7:
|
|
||||||
// A spurious signal occurred. Signals are still disabled here, but will need to be re-enabled.
|
|
||||||
|
|
||||||
// restore stack
|
|
||||||
ld sp, ({tcb_sa_off} + {sa_tmp_sp})(t0)
|
|
||||||
|
|
||||||
// move saved IP away from control, allowing arch_pre to save us if interrupted.
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_saved_ip})(t0)
|
|
||||||
sd t1, ({tcb_sa_off} + {sa_tmp_ip})(t0)
|
|
||||||
|
|
||||||
// restore regs
|
|
||||||
ld t2, ({tcb_sa_off} + {sa_tmp_t2})(t0)
|
|
||||||
ld t3, ({tcb_sa_off} + {sa_tmp_t3})(t0)
|
|
||||||
ld t4, ({tcb_sa_off} + {sa_tmp_t4})(t0)
|
|
||||||
|
|
||||||
// move saved t0 away from control as well
|
|
||||||
mv t1, t0
|
|
||||||
ld t0, ({tcb_sc_off} + {sc_saved_t0})(t0)
|
|
||||||
|
|
||||||
// Re-enable signals. This code can be interrupted after this signal, so we need to define
|
|
||||||
// 'crit_third'.
|
|
||||||
ld gp, ({tcb_sc_off} + {sc_control})(t1)
|
|
||||||
andi gp, gp, ~1
|
|
||||||
sd gp, ({tcb_sc_off} + {sc_control})(t1)
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_third
|
|
||||||
__relibc_internal_sigentry_crit_third:
|
|
||||||
ld gp, ({tcb_sa_off} + {sa_tmp_ip})(t1)
|
|
||||||
.globl __relibc_internal_sigentry_crit_fourth
|
|
||||||
__relibc_internal_sigentry_crit_fourth:
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1)
|
|
||||||
.globl __relibc_internal_sigentry_crit_fifth
|
|
||||||
__relibc_internal_sigentry_crit_fifth:
|
|
||||||
jr gp
|
|
||||||
"] <= [
|
|
||||||
tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)),
|
|
||||||
sc_word = const offset_of!(Sigcontrol, word),
|
|
||||||
sc_saved_t0 = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
||||||
sc_saved_ip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
||||||
sc_control = const offset_of!(Sigcontrol, control_flags),
|
|
||||||
|
|
||||||
tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)),
|
|
||||||
sa_off_pctl = const offset_of!(SigArea, pctl),
|
|
||||||
sa_tmp_sp = const offset_of!(SigArea, tmp_sp),
|
|
||||||
sa_tmp_t1 = const offset_of!(SigArea, tmp_t1),
|
|
||||||
sa_tmp_t2 = const offset_of!(SigArea, tmp_t2),
|
|
||||||
sa_tmp_t3 = const offset_of!(SigArea, tmp_t3),
|
|
||||||
sa_tmp_t4 = const offset_of!(SigArea, tmp_t4),
|
|
||||||
sa_tmp_a0 = const offset_of!(SigArea, tmp_a0),
|
|
||||||
sa_tmp_a1 = const offset_of!(SigArea, tmp_a1),
|
|
||||||
sa_tmp_a2 = const offset_of!(SigArea, tmp_a2),
|
|
||||||
sa_tmp_a3 = const offset_of!(SigArea, tmp_a3),
|
|
||||||
sa_tmp_a4 = const offset_of!(SigArea, tmp_a4),
|
|
||||||
sa_tmp_a7 = const offset_of!(SigArea, tmp_a7),
|
|
||||||
sa_tmp_ip = const offset_of!(SigArea, tmp_ip),
|
|
||||||
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
||||||
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
||||||
sa_altstack_top = const offset_of!(SigArea, altstack_top),
|
|
||||||
sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom),
|
|
||||||
|
|
||||||
pctl_off_actions = const offset_of!(SigProcControl, actions),
|
|
||||||
inner = sym inner_c,
|
|
||||||
pctl_off_pending = const offset_of!(SigProcControl, pending),
|
|
||||||
pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos),
|
|
||||||
SYS_CALL = const syscall::SYS_CALL,
|
|
||||||
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
||||||
proc_fd = sym PROC_FD,
|
|
||||||
]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_rlct_clone_ret: ["
|
|
||||||
ld t0, 0(sp)
|
|
||||||
ld a0, 8(sp)
|
|
||||||
ld a1, 16(sp)
|
|
||||||
ld a2, 24(sp)
|
|
||||||
ld a3, 32(sp)
|
|
||||||
ld a4, 40(sp)
|
|
||||||
ld a5, 48(sp)
|
|
||||||
addi sp, sp, 56
|
|
||||||
|
|
||||||
jalr t0
|
|
||||||
ret
|
|
||||||
"] <= []);
|
|
||||||
|
|
||||||
pub fn current_sp() -> usize {
|
|
||||||
let sp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"mv {}, sp",
|
|
||||||
out(reg) sp,
|
|
||||||
options(nomem));
|
|
||||||
}
|
|
||||||
sp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn manually_enter_trampoline() {
|
|
||||||
let ctl = unsafe { &Tcb::current().unwrap().os_specific.control };
|
|
||||||
|
|
||||||
ctl.control_flags.store(
|
|
||||||
ctl.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(),
|
|
||||||
Ordering::Release,
|
|
||||||
);
|
|
||||||
ctl.saved_archdep_reg.set(0);
|
|
||||||
let ip_location = &ctl.saved_ip as *const _ as usize;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("
|
|
||||||
jal 2f
|
|
||||||
j 3f
|
|
||||||
2:
|
|
||||||
sd ra, 0(t0)
|
|
||||||
la t0, __relibc_internal_sigentry
|
|
||||||
jalr x0, t0
|
|
||||||
3:
|
|
||||||
",
|
|
||||||
inout("t0") ip_location => _, out("ra") _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn __relibc_internal_sigentry_crit_first();
|
|
||||||
fn __relibc_internal_sigentry_crit_second();
|
|
||||||
fn __relibc_internal_sigentry_crit_third();
|
|
||||||
fn __relibc_internal_sigentry_crit_fourth();
|
|
||||||
fn __relibc_internal_sigentry_crit_fifth();
|
|
||||||
}
|
|
||||||
pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt {
|
|
||||||
// It is impossible to update SP and PC atomically. Instead, we abuse the fact that
|
|
||||||
// signals are disabled in the prologue of the signal trampoline, which allows us to emulate
|
|
||||||
// atomicity inside the critical section, consisting of one instruction at 'crit_first', and
|
|
||||||
// one at 'crit_second', see asm.
|
|
||||||
|
|
||||||
if stack.regs.pc == __relibc_internal_sigentry_crit_first as *const () as u64 {
|
|
||||||
// Reexecute 'ld sp, (1 * 8)(sp)'
|
|
||||||
let stack_ptr = stack.regs.int_regs[1] as *const u64; // x2
|
|
||||||
stack.regs.int_regs[1] = unsafe { stack_ptr.add(1).read() };
|
|
||||||
// and 'jr gp' steps.
|
|
||||||
stack.regs.pc = stack.regs.int_regs[2];
|
|
||||||
} else if stack.regs.pc == __relibc_internal_sigentry_crit_second as *const () as u64
|
|
||||||
|| stack.regs.pc == __relibc_internal_sigentry_crit_fifth as *const () as u64
|
|
||||||
{
|
|
||||||
// just reexecute the jump
|
|
||||||
stack.regs.pc = stack.regs.int_regs[2];
|
|
||||||
} else if stack.regs.pc == __relibc_internal_sigentry_crit_third as *const () as u64 {
|
|
||||||
// ld gp, ({tcb_sa_off} + {sa_tmp_ip})(t1)
|
|
||||||
stack.regs.int_regs[2] = area.tmp_ip;
|
|
||||||
// ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1)
|
|
||||||
stack.regs.int_regs[5] = area.tmp_t1;
|
|
||||||
// j gp
|
|
||||||
stack.regs.pc = stack.regs.int_regs[2];
|
|
||||||
} else if stack.regs.pc == __relibc_internal_sigentry_crit_fourth as *const () as u64 {
|
|
||||||
// ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1)
|
|
||||||
stack.regs.int_regs[5] = area.tmp_t1;
|
|
||||||
// jr gp
|
|
||||||
stack.regs.pc = stack.regs.int_regs[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
get_sigaltstack(area, stack.regs.int_regs[1] as usize).into()
|
|
||||||
}
|
|
||||||
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
||||||
let orig_pc = core::mem::replace(
|
|
||||||
&mut stack.regs.pc,
|
|
||||||
__relibc_internal_sigentry as *const () as u64,
|
|
||||||
);
|
|
||||||
control.saved_ip.set(orig_pc as usize);
|
|
||||||
control
|
|
||||||
.saved_archdep_reg
|
|
||||||
.set(stack.regs.int_regs[4] as usize); // t0
|
|
||||||
}
|
|
||||||
pub(crate) static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
||||||
static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize];
|
|
||||||
@@ -1,535 +0,0 @@
|
|||||||
use core::{
|
|
||||||
cell::SyncUnsafeCell,
|
|
||||||
mem::offset_of,
|
|
||||||
ptr::NonNull,
|
|
||||||
sync::atomic::{AtomicU8, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
use syscall::{
|
|
||||||
data::{SigProcControl, Sigcontrol},
|
|
||||||
error::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Tcb,
|
|
||||||
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
||||||
signal::{PROC_CONTROL_STRUCT, PosixStackt, RtSigarea, SigStack, get_sigaltstack, inner_c},
|
|
||||||
};
|
|
||||||
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
||||||
|
|
||||||
use super::ForkScratchpad;
|
|
||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
||||||
pub const STACK_TOP: usize = 1 << 47;
|
|
||||||
pub const STACK_SIZE: usize = 8 * 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SigArea {
|
|
||||||
pub tmp_rip: usize,
|
|
||||||
pub tmp_rsp: usize,
|
|
||||||
pub tmp_rax: usize,
|
|
||||||
pub tmp_rdx: usize,
|
|
||||||
pub tmp_rdi: usize,
|
|
||||||
pub tmp_rsi: usize,
|
|
||||||
pub tmp_r8: usize,
|
|
||||||
pub tmp_r10: usize,
|
|
||||||
pub tmp_r12: usize,
|
|
||||||
pub tmp_rt_inf: RtSigInfo,
|
|
||||||
pub tmp_id_inf: u64,
|
|
||||||
|
|
||||||
pub altstack_top: usize,
|
|
||||||
pub altstack_bottom: usize,
|
|
||||||
pub disable_signals_depth: u64,
|
|
||||||
pub last_sig_was_restart: bool,
|
|
||||||
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C, align(16))]
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct ArchIntRegs {
|
|
||||||
pub ymm_upper: [u128; 16],
|
|
||||||
pub fxsave: [u128; 29],
|
|
||||||
pub r15: usize, // fxsave "available" +0
|
|
||||||
pub r14: usize, // available +8
|
|
||||||
pub r13: usize, // available +16
|
|
||||||
pub r12: usize, // available +24
|
|
||||||
pub rbp: usize, // available +32
|
|
||||||
pub rbx: usize, // available +40
|
|
||||||
pub r11: usize, // outside fxsave, and so on
|
|
||||||
pub r10: usize,
|
|
||||||
pub r9: usize,
|
|
||||||
pub r8: usize,
|
|
||||||
pub rax: usize,
|
|
||||||
pub rcx: usize,
|
|
||||||
pub rdx: usize,
|
|
||||||
pub rsi: usize,
|
|
||||||
pub rdi: usize,
|
|
||||||
pub rflags: usize,
|
|
||||||
pub rip: usize,
|
|
||||||
pub rsp: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
||||||
/// is already initialized as if it was a thread.
|
|
||||||
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file = open_via_dup.dup_into_upper(b"regs/env")?;
|
|
||||||
|
|
||||||
env.fsbase = 0;
|
|
||||||
env.gsbase = 0;
|
|
||||||
|
|
||||||
file.write(&env)?;
|
|
||||||
crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "sysv64" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
||||||
Error::mux(fork_inner(initial_rsp, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
unsafe extern "sysv64" fn child_hook(scratchpad: &ForkScratchpad) {
|
|
||||||
let _ = crate::sys::close(scratchpad.cur_filetable_fd);
|
|
||||||
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
||||||
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
||||||
},
|
|
||||||
new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd)
|
|
||||||
.to_upper()
|
|
||||||
.unwrap(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
push rbx
|
|
||||||
push rbp
|
|
||||||
push r12
|
|
||||||
push r13
|
|
||||||
push r14
|
|
||||||
push r15
|
|
||||||
|
|
||||||
sub rsp, 16
|
|
||||||
|
|
||||||
stmxcsr [rsp+16]
|
|
||||||
fnstcw [rsp+8]
|
|
||||||
|
|
||||||
// rdi: &ForkArgs
|
|
||||||
// rsi: initial_rsp
|
|
||||||
mov rsi, rsp
|
|
||||||
call {fork_impl}
|
|
||||||
|
|
||||||
add rsp, 64
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
"] <= [fork_impl = sym fork_impl]);
|
|
||||||
asmfunction!(__relibc_internal_fork_ret: ["
|
|
||||||
# scratchpad is in rsi, move to rdi for child_hook
|
|
||||||
mov rdi, rsi
|
|
||||||
|
|
||||||
call {child_hook}
|
|
||||||
|
|
||||||
ldmxcsr [rsp + 16]
|
|
||||||
mov rcx, [rsp + 8]
|
|
||||||
|
|
||||||
xor rax, rax
|
|
||||||
|
|
||||||
add rsp, 16
|
|
||||||
pop r15
|
|
||||||
pop r14
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbp
|
|
||||||
pop rbx
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
"] <= [child_hook = sym child_hook]);
|
|
||||||
asmfunction!(__relibc_internal_rlct_clone_ret: ["
|
|
||||||
# Load registers
|
|
||||||
pop rax
|
|
||||||
pop rdi
|
|
||||||
pop rsi
|
|
||||||
pop rdx
|
|
||||||
pop rcx
|
|
||||||
pop r8
|
|
||||||
pop r9
|
|
||||||
|
|
||||||
mov DWORD PTR [rsp - 8], 0x00001F80
|
|
||||||
ldmxcsr [rsp - 8]
|
|
||||||
mov WORD PTR [rsp - 8], 0x037F
|
|
||||||
fldcw [rsp - 8]
|
|
||||||
|
|
||||||
# Call entry point
|
|
||||||
call rax
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= []);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_sigentry: ["
|
|
||||||
// Save some registers
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rsp}], rsp
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rax}], rax
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rdx}], rdx
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rdi}], rdi
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rsi}], rsi
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_r8}], r8
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_r10}], r10
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r12
|
|
||||||
|
|
||||||
// First, select signal, always pick first available bit
|
|
||||||
1:
|
|
||||||
// Read standard signal word - first targeting this thread
|
|
||||||
mov rax, fs:[{tcb_sc_off} + {sc_word}]
|
|
||||||
mov rdx, rax
|
|
||||||
shr rdx, 32
|
|
||||||
and eax, edx
|
|
||||||
bsf eax, eax
|
|
||||||
jnz 2f
|
|
||||||
|
|
||||||
// If no unblocked thread signal was found, check for process.
|
|
||||||
// This is competitive; we need to atomically check if *we* cleared the process-wide pending
|
|
||||||
// bit, otherwise restart.
|
|
||||||
mov eax, [rip + {pctl} + {pctl_off_pending}]
|
|
||||||
and eax, edx
|
|
||||||
bsf eax, eax
|
|
||||||
jz 8f
|
|
||||||
lea rdi, [rip + {pctl} + {pctl_off_sender_infos}]
|
|
||||||
mov rdi, [rdi + rax * 8]
|
|
||||||
lock btr [rip + {pctl} + {pctl_off_pending}], eax
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_id_inf}], rdi
|
|
||||||
jc 9f
|
|
||||||
8:
|
|
||||||
// Read second signal word - both process and thread simultaneously.
|
|
||||||
// This must be done since POSIX requires low realtime signals to be picked first.
|
|
||||||
mov edx, fs:[{tcb_sc_off} + {sc_word} + 8]
|
|
||||||
mov eax, [rip + {pctl} + {pctl_off_pending} + 4]
|
|
||||||
or eax, edx
|
|
||||||
and eax, fs:[{tcb_sc_off} + {sc_word} + 12]
|
|
||||||
bsf eax, eax
|
|
||||||
jz 7f
|
|
||||||
|
|
||||||
bt edx, eax // check if signal was sent to thread specifically
|
|
||||||
jnc 81f
|
|
||||||
// if so, continue as usual
|
|
||||||
add eax, 32
|
|
||||||
jmp 2f
|
|
||||||
81:
|
|
||||||
// otherwise, try (competitively) dequeueing realtime signal
|
|
||||||
|
|
||||||
// SYS_CALL(fd, payload_base, payload_len, metadata_len | (flags << 8), metadata_base)
|
|
||||||
// rax rdi rsi rdx r10 r8
|
|
||||||
|
|
||||||
mov r12d, eax
|
|
||||||
mov rsi, fs:[0]
|
|
||||||
mov rdi, [rip+{proc_fd}]
|
|
||||||
add rsi, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig
|
|
||||||
mov rdx, {RTINF_SIZE}
|
|
||||||
mov [rsi], eax
|
|
||||||
lea r8, [rip + {proc_call_sigdeq}]
|
|
||||||
mov r10, 1
|
|
||||||
mov eax, {SYS_CALL}
|
|
||||||
syscall
|
|
||||||
test eax, eax
|
|
||||||
jnz 1b // assumes error can only be EAGAIN
|
|
||||||
lea eax, [r12d + 32]
|
|
||||||
jmp 9f
|
|
||||||
2:
|
|
||||||
mov edx, eax
|
|
||||||
shr edx, 5
|
|
||||||
mov rdi, fs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8]
|
|
||||||
lock btr fs:[{tcb_sc_off} + {sc_word} + edx * 4], eax
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_id_inf}], rdi
|
|
||||||
add eax, 64 // indicate signal was targeted at thread
|
|
||||||
9:
|
|
||||||
sub rsp, {REDZONE_SIZE}
|
|
||||||
and rsp, -{STACK_ALIGN}
|
|
||||||
|
|
||||||
// By now we have selected a signal, stored in eax (6-bit). We now need to choose whether or
|
|
||||||
// not to switch to the alternate signal stack. If SA_ONSTACK is clear for this signal, then
|
|
||||||
// skip the sigaltstack logic.
|
|
||||||
lea rdx, [rip + {pctl} + {pctl_off_actions}]
|
|
||||||
|
|
||||||
mov ecx, eax
|
|
||||||
and ecx, 63
|
|
||||||
|
|
||||||
// LEA doesn't support 16x, so just do two x8s.
|
|
||||||
lea rdx, [rdx + 8 * rcx]
|
|
||||||
lea rdx, [rdx + 8 * rcx]
|
|
||||||
|
|
||||||
bt qword ptr [rdx], {SA_ONSTACK_BIT}
|
|
||||||
jnc 4f
|
|
||||||
|
|
||||||
// Otherwise, the altstack is already active. The sigaltstack being disabled, is equivalent
|
|
||||||
// to setting 'top' to usize::MAX and 'bottom' to 0.
|
|
||||||
|
|
||||||
// If current RSP is above altstack region, switch to altstack
|
|
||||||
mov rdx, fs:[{tcb_sa_off} + {sa_altstack_top}]
|
|
||||||
cmp rsp, rdx
|
|
||||||
cmova rsp, rdx
|
|
||||||
|
|
||||||
// If current RSP is below altstack region, also switch to altstack
|
|
||||||
cmp rsp, fs:[{tcb_sa_off} + {sa_altstack_bottom}]
|
|
||||||
cmovbe rsp, rdx
|
|
||||||
|
|
||||||
.p2align 4
|
|
||||||
4:
|
|
||||||
// Now that we have a stack, we can finally start initializing the signal stack!
|
|
||||||
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rsp}]
|
|
||||||
push fs:[{tcb_sc_off} + {sc_saved_rip}]
|
|
||||||
push fs:[{tcb_sc_off} + {sc_saved_rflags}]
|
|
||||||
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rdi}]
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rsi}]
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rdx}]
|
|
||||||
push rcx
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rax}]
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_r8}]
|
|
||||||
push r9
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_r10}]
|
|
||||||
push r11
|
|
||||||
push rbx
|
|
||||||
push rbp
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_r12}]
|
|
||||||
push r13
|
|
||||||
push r14
|
|
||||||
push r15
|
|
||||||
sub rsp, (29 + 16) * 16 // fxsave region minus available bytes
|
|
||||||
fxsave64 [rsp + 16 * 16]
|
|
||||||
|
|
||||||
// TODO: self-modifying?
|
|
||||||
cmp byte ptr [rip + {supports_avx}], 0
|
|
||||||
je 5f
|
|
||||||
|
|
||||||
// Prefer vextractf128 over vextracti128 since the former only requires AVX version 1.
|
|
||||||
vextractf128 [rsp + 15 * 16], ymm0, 1
|
|
||||||
vextractf128 [rsp + 14 * 16], ymm1, 1
|
|
||||||
vextractf128 [rsp + 13 * 16], ymm2, 1
|
|
||||||
vextractf128 [rsp + 12 * 16], ymm3, 1
|
|
||||||
vextractf128 [rsp + 11 * 16], ymm4, 1
|
|
||||||
vextractf128 [rsp + 10 * 16], ymm5, 1
|
|
||||||
vextractf128 [rsp + 9 * 16], ymm6, 1
|
|
||||||
vextractf128 [rsp + 8 * 16], ymm7, 1
|
|
||||||
vextractf128 [rsp + 7 * 16], ymm8, 1
|
|
||||||
vextractf128 [rsp + 6 * 16], ymm9, 1
|
|
||||||
vextractf128 [rsp + 5 * 16], ymm10, 1
|
|
||||||
vextractf128 [rsp + 4 * 16], ymm11, 1
|
|
||||||
vextractf128 [rsp + 3 * 16], ymm12, 1
|
|
||||||
vextractf128 [rsp + 2 * 16], ymm13, 1
|
|
||||||
vextractf128 [rsp + 16], ymm14, 1
|
|
||||||
vextractf128 [rsp], ymm15, 1
|
|
||||||
5:
|
|
||||||
mov [rsp - 4], eax
|
|
||||||
sub rsp, 64 // alloc space for ucontext fields
|
|
||||||
|
|
||||||
mov rdi, rsp
|
|
||||||
call {inner}
|
|
||||||
|
|
||||||
add rsp, 64
|
|
||||||
|
|
||||||
fxrstor64 [rsp + 16 * 16]
|
|
||||||
|
|
||||||
cmp byte ptr [rip + {supports_avx}], 0
|
|
||||||
je 6f
|
|
||||||
|
|
||||||
vinsertf128 ymm0, ymm0, [rsp + 15 * 16], 1
|
|
||||||
vinsertf128 ymm1, ymm1, [rsp + 14 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 13 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 12 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 11 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 10 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 9 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 8 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 7 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 6 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 5 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 4 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 3 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 2 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp], 1
|
|
||||||
6:
|
|
||||||
add rsp, (29 + 16) * 16
|
|
||||||
|
|
||||||
pop r15
|
|
||||||
pop r14
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbp
|
|
||||||
pop rbx
|
|
||||||
pop r11
|
|
||||||
pop r10
|
|
||||||
pop r9
|
|
||||||
pop r8
|
|
||||||
pop rax
|
|
||||||
pop rcx
|
|
||||||
pop rdx
|
|
||||||
pop rsi
|
|
||||||
pop rdi
|
|
||||||
|
|
||||||
popfq
|
|
||||||
pop qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}]
|
|
||||||
|
|
||||||
// x86 lacks atomic instructions for setting both the stack and instruction pointer
|
|
||||||
// simultaneously, except the slow microcoded IRETQ instruction. Thus, we let the arch_pre
|
|
||||||
// function emulate atomicity between the pop rsp and indirect jump.
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_first
|
|
||||||
__relibc_internal_sigentry_crit_first:
|
|
||||||
|
|
||||||
pop rsp
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_second
|
|
||||||
__relibc_internal_sigentry_crit_second:
|
|
||||||
jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}]
|
|
||||||
7:
|
|
||||||
// A spurious signal occurred. Signals are still disabled here, but will need to be re-enabled.
|
|
||||||
|
|
||||||
// restore flags
|
|
||||||
mov rax, fs:[0] // load FS base
|
|
||||||
// TODO: Use lahf/sahf rather than pushfq/popfq?
|
|
||||||
lea rsp, [rax + {tcb_sc_off} + {sc_saved_rflags}]
|
|
||||||
popfq
|
|
||||||
|
|
||||||
// restore stack
|
|
||||||
mov rsp, fs:[{tcb_sa_off} + {sa_tmp_rsp}]
|
|
||||||
|
|
||||||
// move saved RIP away from control, allowing arch_pre to save us if interrupted.
|
|
||||||
mov rax, fs:[{tcb_sc_off} + {sc_saved_rip}]
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rip}], rax
|
|
||||||
|
|
||||||
// restore regs
|
|
||||||
mov rax, fs:[{tcb_sa_off} + {sa_tmp_rax}]
|
|
||||||
mov rdx, fs:[{tcb_sa_off} + {sa_tmp_rdx}]
|
|
||||||
|
|
||||||
// Re-enable signals. This code can be interrupted after this signal, so we need to define
|
|
||||||
// 'crit_third'.
|
|
||||||
and qword ptr fs:[{tcb_sc_off} + {sc_control}], ~1
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_third
|
|
||||||
__relibc_internal_sigentry_crit_third:
|
|
||||||
jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}]
|
|
||||||
"] <= [
|
|
||||||
inner = sym inner_c,
|
|
||||||
sa_tmp_rip = const offset_of!(SigArea, tmp_rip),
|
|
||||||
sa_tmp_rsp = const offset_of!(SigArea, tmp_rsp),
|
|
||||||
sa_tmp_rax = const offset_of!(SigArea, tmp_rax),
|
|
||||||
sa_tmp_rdx = const offset_of!(SigArea, tmp_rdx),
|
|
||||||
sa_tmp_rdi = const offset_of!(SigArea, tmp_rdi),
|
|
||||||
sa_tmp_rsi = const offset_of!(SigArea, tmp_rsi),
|
|
||||||
sa_tmp_r8 = const offset_of!(SigArea, tmp_r8),
|
|
||||||
sa_tmp_r10 = const offset_of!(SigArea, tmp_r10),
|
|
||||||
sa_tmp_r12 = const offset_of!(SigArea, tmp_r12),
|
|
||||||
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
||||||
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
||||||
sa_altstack_top = const offset_of!(SigArea, altstack_top),
|
|
||||||
sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom),
|
|
||||||
sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
||||||
sc_saved_rip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
sc_word = const offset_of!(Sigcontrol, word),
|
|
||||||
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
||||||
sc_control = const offset_of!(Sigcontrol, control_flags),
|
|
||||||
tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch),
|
|
||||||
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
||||||
pctl_off_actions = const offset_of!(SigProcControl, actions),
|
|
||||||
pctl_off_pending = const offset_of!(SigProcControl, pending),
|
|
||||||
pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos),
|
|
||||||
pctl = sym PROC_CONTROL_STRUCT,
|
|
||||||
supports_avx = sym SUPPORTS_AVX,
|
|
||||||
REDZONE_SIZE = const 128,
|
|
||||||
STACK_ALIGN = const 16,
|
|
||||||
SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000
|
|
||||||
SYS_CALL = const syscall::SYS_CALL,
|
|
||||||
proc_call_sigdeq = sym PROC_CALL_SIGDEQ,
|
|
||||||
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
||||||
proc_fd = sym PROC_FD,
|
|
||||||
]);
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn __relibc_internal_sigentry_crit_first();
|
|
||||||
fn __relibc_internal_sigentry_crit_second();
|
|
||||||
fn __relibc_internal_sigentry_crit_third();
|
|
||||||
}
|
|
||||||
/// Fixes some edge cases, and calculates the value for uc_stack.
|
|
||||||
pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt {
|
|
||||||
// It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is
|
|
||||||
// almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that
|
|
||||||
// signals are disabled in the prologue of the signal trampoline, which allows us to emulate
|
|
||||||
// atomicity inside the critical section, consisting of one instruction at 'crit_first', one at
|
|
||||||
// 'crit_second', and one at 'crit_third', see asm.
|
|
||||||
|
|
||||||
if stack.regs.rip == __relibc_internal_sigentry_crit_first as *const () as usize {
|
|
||||||
// Reexecute pop rsp and jump steps. This case needs to be different from the one below,
|
|
||||||
// since rsp has not been overwritten with the previous context's stack, just yet. At this
|
|
||||||
// point, we know [rsp+0] contains the saved RSP, and [rsp-8] contains the saved RIP.
|
|
||||||
let stack_ptr = stack.regs.rsp as *const usize;
|
|
||||||
stack.regs.rsp = unsafe { stack_ptr.read() };
|
|
||||||
stack.regs.rip = unsafe { stack_ptr.sub(1).read() };
|
|
||||||
} else if stack.regs.rip == __relibc_internal_sigentry_crit_second as *const () as usize
|
|
||||||
|| stack.regs.rip == __relibc_internal_sigentry_crit_third as *const () as usize
|
|
||||||
{
|
|
||||||
// Almost finished, just reexecute the jump before tmp_rip is overwritten by this
|
|
||||||
// deeper-level signal.
|
|
||||||
stack.regs.rip = area.tmp_rip;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_sigaltstack(area, stack.regs.rsp).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rearrange the restore-stack and sigarea in a way that makes it look like a new signal was
|
|
||||||
/// immediately delivered after restoring the allowset to what it was prior to the original signal delivery.
|
|
||||||
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
||||||
let orig_rip = core::mem::replace(
|
|
||||||
&mut stack.regs.rip,
|
|
||||||
__relibc_internal_sigentry as *const () as usize,
|
|
||||||
);
|
|
||||||
control.saved_ip.set(orig_rip);
|
|
||||||
control.saved_archdep_reg.set(stack.regs.rflags);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) static SUPPORTS_AVX: AtomicU8 = AtomicU8::new(0);
|
|
||||||
|
|
||||||
// __relibc will be prepended to the name, so no_mangle is fine
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe fn manually_enter_trampoline() {
|
|
||||||
let c = &Tcb::current().unwrap().os_specific.control;
|
|
||||||
c.control_flags.store(
|
|
||||||
c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(),
|
|
||||||
Ordering::Release,
|
|
||||||
);
|
|
||||||
c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86?
|
|
||||||
|
|
||||||
core::arch::asm!("
|
|
||||||
lea rax, [rip + 2f]
|
|
||||||
mov fs:[{tcb_sc_off} + {sc_saved_rip}], rax
|
|
||||||
jmp __relibc_internal_sigentry
|
|
||||||
2:
|
|
||||||
",
|
|
||||||
out("rax") _,
|
|
||||||
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
||||||
sc_saved_rip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get current stack pointer, weak granularity guarantees.
|
|
||||||
pub fn current_sp() -> usize {
|
|
||||||
let sp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mov {}, rsp", out(reg) sp);
|
|
||||||
}
|
|
||||||
sp
|
|
||||||
}
|
|
||||||
|
|
||||||
static PROC_CALL_SIGDEQ: u64 = ProcCall::Sigdeq as u64;
|
|
||||||
pub(crate) static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![allow(internal_features)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
#![feature(core_intrinsics, int_roundings, slice_ptr_get, sync_unsafe_cell)]
|
|
||||||
#![forbid(unreachable_patterns)]
|
|
||||||
|
|
||||||
use core::cell::UnsafeCell;
|
|
||||||
|
|
||||||
use generic_rt::GenericTcb;
|
|
||||||
use redox_protocols::protocol::ProcMeta;
|
|
||||||
use syscall::Sigcontrol;
|
|
||||||
|
|
||||||
use self::{
|
|
||||||
proc::{FdGuard, FdGuardUpper, STATIC_PROC_INFO},
|
|
||||||
sync::Mutex,
|
|
||||||
sys::FdTbl,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! asmfunction(
|
|
||||||
($name:ident $(($($arg:ty),*))? $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => {
|
|
||||||
::core::arch::global_asm!(concat!("
|
|
||||||
.p2align 4
|
|
||||||
.section .text.", stringify!($name), ", \"ax\", @progbits
|
|
||||||
.globl ", stringify!($name), "
|
|
||||||
.type ", stringify!($name), ", @function
|
|
||||||
", stringify!($name), ":
|
|
||||||
", $($asmstmt, "\n",)* "
|
|
||||||
.size ", stringify!($name), ", . - ", stringify!($name), "
|
|
||||||
"), $($decl = $(sym $symname)?$(const $constval)?),*);
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
pub fn $name($($(_: $arg),*)?) $(-> $ret)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
pub mod arch;
|
|
||||||
pub mod proc;
|
|
||||||
|
|
||||||
// TODO: Replace auxvs with a non-stack-based interface, but keep getauxval for compatibility
|
|
||||||
#[path = "../../src/platform/auxv_defs.rs"]
|
|
||||||
pub mod auxv_defs;
|
|
||||||
|
|
||||||
pub mod signal;
|
|
||||||
pub mod sync;
|
|
||||||
pub mod sys;
|
|
||||||
pub mod thread;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct RtTcb {
|
|
||||||
pub control: Sigcontrol,
|
|
||||||
pub arch: UnsafeCell<crate::arch::SigArea>,
|
|
||||||
pub thr_fd: UnsafeCell<Option<FdGuardUpper>>,
|
|
||||||
}
|
|
||||||
impl RtTcb {
|
|
||||||
pub fn current() -> &'static Self {
|
|
||||||
unsafe { &Tcb::current().unwrap().os_specific }
|
|
||||||
}
|
|
||||||
pub fn thread_fd(&self) -> &FdGuardUpper {
|
|
||||||
unsafe { (&*self.thr_fd.get()).as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Tcb = GenericTcb<RtTcb>;
|
|
||||||
|
|
||||||
// TODO: Remove this by keeping the TCB initialized during exec
|
|
||||||
// and informing ld.so through some other way.
|
|
||||||
pub static TLS_ACTIVATED: core::sync::atomic::AtomicBool =
|
|
||||||
core::sync::atomic::AtomicBool::new(false);
|
|
||||||
|
|
||||||
/// OS and architecture specific code to activate TLS - Redox aarch64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) {
|
|
||||||
// Uses ABI page
|
|
||||||
let abi_ptr = tls_end - tls_len - 16;
|
|
||||||
core::ptr::write(abi_ptr as *mut usize, tls_end);
|
|
||||||
core::arch::asm!(
|
|
||||||
"msr tpidr_el0, {}",
|
|
||||||
in(reg) abi_ptr,
|
|
||||||
);
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// OS and architecture specific code to activate TLS - Redox x86
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end: usize, _tls_len: usize) {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file_fd = crate::sys::dup_into_upper_raw(tcb.thread_fd().as_raw_fd(), b"regs/env")
|
|
||||||
.expect("failed to open handle for process registers");
|
|
||||||
|
|
||||||
syscall::read(file_fd, &mut env).expect("failed to read gsbase");
|
|
||||||
|
|
||||||
env.gsbase = tls_end as u32;
|
|
||||||
|
|
||||||
syscall::write(file_fd, &env).expect("failed to write gsbase");
|
|
||||||
|
|
||||||
let _ = crate::sys::close_raw(file_fd);
|
|
||||||
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// OS and architecture specific code to activate TLS - Redox x86_64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end_and_tcb_start: usize, _tls_len: usize) {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file_fd = crate::sys::dup_into_upper_raw(tcb.thread_fd().as_raw_fd(), b"regs/env")
|
|
||||||
.expect("failed to open handle for process registers");
|
|
||||||
|
|
||||||
syscall::read(file_fd, &mut env).expect("failed to read fsbase");
|
|
||||||
|
|
||||||
env.fsbase = tls_end_and_tcb_start as u64;
|
|
||||||
|
|
||||||
syscall::write(file_fd, &env).expect("failed to write fsbase");
|
|
||||||
|
|
||||||
let _ = crate::sys::close_raw(file_fd);
|
|
||||||
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// OS and architecture specific code to activate TLS - Redox riscv64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) {
|
|
||||||
// tp points to static tls block
|
|
||||||
// FIXME limited to a single initial master
|
|
||||||
let tls_start = tls_end - tls_len;
|
|
||||||
let abi_ptr = tls_start - 8;
|
|
||||||
core::ptr::write(abi_ptr as *mut usize, tls_end);
|
|
||||||
core::arch::asm!(
|
|
||||||
"mv tp, {}",
|
|
||||||
in(reg) tls_start
|
|
||||||
);
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize redox-rt in situations where relibc is not used
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(not(feature = "proc"))]
|
|
||||||
pub unsafe fn initialize_freestanding(this_thr_fd: FdGuardUpper) -> &'static FdGuardUpper {
|
|
||||||
// TODO: This code is a hack! Integrate the ld_so TCB code into generic-rt, and then use that
|
|
||||||
// (this function will need pointers to the ELF structs normally passed in auxvs), so the TCB
|
|
||||||
// is initialized properly.
|
|
||||||
|
|
||||||
// TODO: TLS
|
|
||||||
let page = {
|
|
||||||
&mut *(syscall::fmap(
|
|
||||||
!0,
|
|
||||||
&syscall::Map {
|
|
||||||
offset: 0,
|
|
||||||
size: syscall::PAGE_SIZE,
|
|
||||||
flags: syscall::MapFlags::PROT_READ
|
|
||||||
| syscall::MapFlags::PROT_WRITE
|
|
||||||
| syscall::MapFlags::MAP_PRIVATE,
|
|
||||||
address: 0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap() as *mut Tcb)
|
|
||||||
};
|
|
||||||
page.tcb_ptr = page;
|
|
||||||
page.tcb_len = syscall::PAGE_SIZE;
|
|
||||||
page.tls_end = (page as *mut Tcb).cast();
|
|
||||||
|
|
||||||
let cur_filetable_fd = syscall::dup_into(
|
|
||||||
this_thr_fd.as_raw_fd(),
|
|
||||||
this_thr_fd.as_raw_fd() + 1,
|
|
||||||
b"filetable-binary",
|
|
||||||
)
|
|
||||||
.expect("failed to open filetable-binary");
|
|
||||||
|
|
||||||
*current_filetable() =
|
|
||||||
FdTbl::from_binary_fd(FdGuard::new(cur_filetable_fd).to_upper().unwrap())
|
|
||||||
.expect("failed to populate fds");
|
|
||||||
|
|
||||||
// Make sure to use ptr::write to prevent dropping the existing FdGuard
|
|
||||||
page.os_specific.thr_fd.get().write(Some(this_thr_fd));
|
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
|
|
||||||
unsafe {
|
|
||||||
let tcb_addr = page as *mut Tcb as usize;
|
|
||||||
tcb_activate(&page.os_specific, tcb_addr, 0)
|
|
||||||
}
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
unsafe {
|
|
||||||
let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr);
|
|
||||||
core::arch::asm!("msr tpidr_el0, {}", in(reg) abi_ptr);
|
|
||||||
}
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
unsafe {
|
|
||||||
let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr) as usize;
|
|
||||||
core::arch::asm!("mv tp, {}", in(reg) (abi_ptr + 8));
|
|
||||||
}
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
(*page.os_specific.thr_fd.get()).as_ref().unwrap()
|
|
||||||
}
|
|
||||||
pub(crate) fn read_proc_meta(proc: &FdGuardUpper) -> syscall::Result<ProcMeta> {
|
|
||||||
let mut bytes = [0_u8; size_of::<ProcMeta>()];
|
|
||||||
proc.read(&mut bytes)?;
|
|
||||||
Ok(*plain::from_bytes::<ProcMeta>(&bytes).unwrap())
|
|
||||||
}
|
|
||||||
pub unsafe fn initialize(
|
|
||||||
#[cfg(feature = "proc")] proc_fd: FdGuardUpper,
|
|
||||||
#[cfg(feature = "proc")] ns_fd: Option<FdGuardUpper>,
|
|
||||||
) {
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
let metadata = read_proc_meta(&proc_fd).unwrap();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "proc"))]
|
|
||||||
// Bootstrap mode, don't associate proc fds with PIDs
|
|
||||||
let metadata = ProcMeta::default();
|
|
||||||
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
{
|
|
||||||
unsafe { crate::arch::PROC_FD.get().write(proc_fd.as_raw_fd()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
STATIC_PROC_INFO.get().write(StaticProcInfo {
|
|
||||||
pid: metadata.pid,
|
|
||||||
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
proc_fd: Some(proc_fd),
|
|
||||||
|
|
||||||
#[cfg(not(feature = "proc"))]
|
|
||||||
proc_fd: None,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
{
|
|
||||||
*DYNAMIC_PROC_INFO.lock() = DynamicProcInfo {
|
|
||||||
pgid: metadata.pgid,
|
|
||||||
ruid: metadata.ruid,
|
|
||||||
euid: metadata.euid,
|
|
||||||
suid: metadata.suid,
|
|
||||||
egid: metadata.egid,
|
|
||||||
rgid: metadata.rgid,
|
|
||||||
sgid: metadata.sgid,
|
|
||||||
ns_fd,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct StaticProcInfo {
|
|
||||||
pid: u32,
|
|
||||||
proc_fd: Option<FdGuardUpper>,
|
|
||||||
}
|
|
||||||
pub struct DynamicProcInfo {
|
|
||||||
pub pgid: u32,
|
|
||||||
pub euid: u32,
|
|
||||||
pub suid: u32,
|
|
||||||
pub ruid: u32,
|
|
||||||
pub egid: u32,
|
|
||||||
pub rgid: u32,
|
|
||||||
pub sgid: u32,
|
|
||||||
pub ns_fd: Option<FdGuardUpper>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static DYNAMIC_PROC_INFO: Mutex<DynamicProcInfo> = Mutex::new(DynamicProcInfo {
|
|
||||||
pgid: u32::MAX,
|
|
||||||
ruid: u32::MAX,
|
|
||||||
euid: u32::MAX,
|
|
||||||
suid: u32::MAX,
|
|
||||||
rgid: u32::MAX,
|
|
||||||
egid: u32::MAX,
|
|
||||||
sgid: u32::MAX,
|
|
||||||
ns_fd: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn static_proc_info() -> &'static StaticProcInfo {
|
|
||||||
unsafe { &*STATIC_PROC_INFO.get() }
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn current_proc_fd() -> &'static FdGuardUpper {
|
|
||||||
let info = static_proc_info();
|
|
||||||
info.proc_fd.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn current_namespace_fd() -> syscall::Result<usize> {
|
|
||||||
DYNAMIC_PROC_INFO
|
|
||||||
.lock()
|
|
||||||
.ns_fd
|
|
||||||
.as_ref()
|
|
||||||
.map(|g| g.as_raw_fd())
|
|
||||||
.ok_or(syscall::Error::new(syscall::ENOENT))
|
|
||||||
}
|
|
||||||
struct ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard,
|
|
||||||
new_proc_fd: Option<FdGuard>,
|
|
||||||
new_filetable_fd: FdGuardUpper,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn child_hook_common(args: ChildHookCommonArgs) {
|
|
||||||
let new_filetable_fd = args.new_filetable_fd;
|
|
||||||
|
|
||||||
let old_filetable_fd;
|
|
||||||
{
|
|
||||||
let mut guard = current_filetable();
|
|
||||||
old_filetable_fd = guard.take();
|
|
||||||
guard.set_fd(new_filetable_fd);
|
|
||||||
guard
|
|
||||||
.override_at(args.new_thr_fd.as_raw_fd(), args.new_thr_fd.as_raw_fd())
|
|
||||||
.expect("failed to add new_thr_fd");
|
|
||||||
if let Some(new_proc_fd) = args.new_proc_fd.as_ref() {
|
|
||||||
guard
|
|
||||||
.override_at(new_proc_fd.as_raw_fd(), new_proc_fd.as_raw_fd())
|
|
||||||
.expect("failed to add new_proc_fd");
|
|
||||||
}
|
|
||||||
if let Some(ref old) = old_filetable_fd {
|
|
||||||
let _ = guard.remove(old.as_raw_fd());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_thr_fd = args.new_thr_fd.to_upper().unwrap();
|
|
||||||
let new_proc_fd = args.new_proc_fd.map(|x| x.to_upper().unwrap());
|
|
||||||
|
|
||||||
// TODO: just pass PID to child rather than obtaining it via IPC?
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
let metadata = read_proc_meta(
|
|
||||||
new_proc_fd
|
|
||||||
.as_ref()
|
|
||||||
.expect("must be present with proc feature"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "proc"))]
|
|
||||||
let metadata = ProcMeta::default();
|
|
||||||
|
|
||||||
if let Some(proc_fd) = &new_proc_fd {
|
|
||||||
unsafe { crate::arch::PROC_FD.get().write(proc_fd.as_raw_fd()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
let old_proc_fd = unsafe {
|
|
||||||
STATIC_PROC_INFO
|
|
||||||
.get()
|
|
||||||
.replace(StaticProcInfo {
|
|
||||||
pid: metadata.pid,
|
|
||||||
proc_fd: new_proc_fd,
|
|
||||||
})
|
|
||||||
.proc_fd
|
|
||||||
};
|
|
||||||
drop(old_proc_fd);
|
|
||||||
|
|
||||||
let old_thr_fd = unsafe { RtTcb::current().thr_fd.get().replace(Some(new_thr_fd)) };
|
|
||||||
drop(old_thr_fd);
|
|
||||||
|
|
||||||
drop(old_filetable_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FILETABLE: Mutex<FdTbl> = Mutex::new(FdTbl::new());
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn current_filetable() -> crate::sync::MutexGuard<'static, FdTbl> {
|
|
||||||
FILETABLE.lock()
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,68 +0,0 @@
|
|||||||
// TODO: Share code for simple futex-based mutex between relibc's Mutex<()> and this.
|
|
||||||
|
|
||||||
use core::{
|
|
||||||
cell::UnsafeCell,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Mutex<T> {
|
|
||||||
pub lockword: AtomicU32,
|
|
||||||
pub inner: UnsafeCell<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for Mutex<T> {}
|
|
||||||
unsafe impl<T: Send> Sync for Mutex<T> {}
|
|
||||||
|
|
||||||
impl<T> Mutex<T> {
|
|
||||||
/// Represents an unlocked [Mutex].
|
|
||||||
pub const UNLOCKED: u32 = 0;
|
|
||||||
/// Represents a locked [Mutex].
|
|
||||||
pub const LOCKED: u32 = 1;
|
|
||||||
/// Represents a waiting [Mutex].
|
|
||||||
pub const WAITING: u32 = 2;
|
|
||||||
|
|
||||||
pub const fn new(t: T) -> Self {
|
|
||||||
Self {
|
|
||||||
lockword: AtomicU32::new(0),
|
|
||||||
inner: UnsafeCell::new(t),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn lock(&self) -> MutexGuard<'_, T> {
|
|
||||||
while self
|
|
||||||
.lockword
|
|
||||||
.compare_exchange(
|
|
||||||
Self::UNLOCKED,
|
|
||||||
Self::LOCKED,
|
|
||||||
Ordering::Acquire,
|
|
||||||
Ordering::Relaxed,
|
|
||||||
)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
core::hint::spin_loop();
|
|
||||||
}
|
|
||||||
MutexGuard { lock: self }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct MutexGuard<'l, T> {
|
|
||||||
lock: &'l Mutex<T>,
|
|
||||||
}
|
|
||||||
impl<T> Deref for MutexGuard<'_, T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
unsafe { &*self.lock.inner.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> DerefMut for MutexGuard<'_, T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
|
||||||
unsafe { &mut *self.lock.inner.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Drop for MutexGuard<'_, T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.lock
|
|
||||||
.lockword
|
|
||||||
.store(Mutex::<T>::UNLOCKED, Ordering::Release);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-2084
File diff suppressed because it is too large
Load Diff
@@ -1,76 +0,0 @@
|
|||||||
use core::mem::size_of;
|
|
||||||
|
|
||||||
use syscall::Result;
|
|
||||||
|
|
||||||
use crate::{RtTcb, arch::*, proc::*, static_proc_info};
|
|
||||||
|
|
||||||
/// Spawns a new context sharing the same address space as the current one (i.e. a new thread).
|
|
||||||
pub unsafe fn rlct_clone_impl(stack: *mut usize, tcb: &RtTcb) -> Result<usize> {
|
|
||||||
let proc_info = static_proc_info();
|
|
||||||
let cur_proc_fd = proc_info.proc_fd.as_ref().unwrap();
|
|
||||||
|
|
||||||
let cur_thr_fd = RtTcb::current().thread_fd();
|
|
||||||
let new_thr_fd = cur_proc_fd.dup_into_upper(b"new-thread")?;
|
|
||||||
|
|
||||||
// Inherit existing address space
|
|
||||||
{
|
|
||||||
let cur_addr_space_fd = cur_thr_fd.dup_into_upper(b"addrspace")?;
|
|
||||||
let new_addr_space_sel_fd = new_thr_fd.dup_into_upper(b"current-addrspace")?;
|
|
||||||
|
|
||||||
let buf = create_set_addr_space_buf(
|
|
||||||
cur_addr_space_fd.as_raw_fd(),
|
|
||||||
__relibc_internal_rlct_clone_ret as *const () as usize,
|
|
||||||
stack as usize,
|
|
||||||
);
|
|
||||||
new_addr_space_sel_fd.write(&buf)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inherit reference to file table
|
|
||||||
{
|
|
||||||
let cur_filetable_fd = cur_thr_fd.dup_into_upper(b"filetable")?;
|
|
||||||
let new_filetable_sel_fd = new_thr_fd.dup_into_upper(b"current-filetable")?;
|
|
||||||
|
|
||||||
new_filetable_sel_fd.write(&usize::to_ne_bytes(cur_filetable_fd.as_raw_fd()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the signal handler is not yet initialized, signals specifically targeting the thread
|
|
||||||
// (relibc is only required to implement thread-specific signals that already originate from
|
|
||||||
// the same process) will be discarded. Process-specific signals will ignore this new thread,
|
|
||||||
// until it has initialized its own signal handler.
|
|
||||||
|
|
||||||
let start_fd = new_thr_fd.dup_into_upper(b"start")?;
|
|
||||||
|
|
||||||
let fd = new_thr_fd.as_raw_fd();
|
|
||||||
unsafe {
|
|
||||||
tcb.thr_fd.get().write(Some(new_thr_fd));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unblock context.
|
|
||||||
start_fd.write(&[0])?;
|
|
||||||
|
|
||||||
Ok(fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn exit_this_thread(stack_base: *mut (), stack_size: usize) -> ! {
|
|
||||||
let _guard = crate::signal::tmp_disable_signals();
|
|
||||||
let thread_fd = RtTcb::current().thread_fd();
|
|
||||||
|
|
||||||
// TODO: modify interface so it writes directly to the thread fd?
|
|
||||||
let status_fd = thread_fd.dup_into_upper(b"status").unwrap();
|
|
||||||
unsafe { exit_this_thread_inner(stack_base, stack_size, status_fd) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// exit_this_thread, but current thread_fd might has disappear
|
|
||||||
pub unsafe fn exit_this_thread_inner(
|
|
||||||
stack_base: *mut (),
|
|
||||||
stack_size: usize,
|
|
||||||
status_fd: FdGuard<true>,
|
|
||||||
) -> ! {
|
|
||||||
let mut buf = [0; size_of::<usize>() * 3];
|
|
||||||
plain::slice_from_mut_bytes(&mut buf)
|
|
||||||
.unwrap()
|
|
||||||
.copy_from_slice(&[usize::MAX, stack_base as usize, stack_size]);
|
|
||||||
// TODO: SYS_CALL w/CONSUME
|
|
||||||
status_fd.write(&buf).unwrap();
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
target=$1
|
|
||||||
deps_dir=$2
|
|
||||||
|
|
||||||
if [ -z "$target" ] || [ -z "$deps_dir" ]; then
|
|
||||||
echo "Usage:\n\t./renamesyms.sh TARGET DEPS_DIR"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$target" ]; then
|
|
||||||
echo "Target file '$target' does not exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ ! -d "$deps_dir" ] ; then
|
|
||||||
echo "Deps dir '$deps_dir' does not exist or not a directory"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
symbols_file=`mktemp`
|
|
||||||
special_syms=(
|
|
||||||
__rdl_oom
|
|
||||||
__rg_alloc
|
|
||||||
__rg_alloc_zeroed
|
|
||||||
__rg_dealloc
|
|
||||||
__rg_oom
|
|
||||||
__rg_realloc
|
|
||||||
__rust_alloc
|
|
||||||
__rust_alloc_error_handler
|
|
||||||
__rust_alloc_error_handler_should_panic
|
|
||||||
__rust_alloc_zeroed
|
|
||||||
__rust_dealloc
|
|
||||||
__rust_no_alloc_shim_is_unstable
|
|
||||||
__rust_realloc
|
|
||||||
)
|
|
||||||
|
|
||||||
for dep in `find $deps_dir -type f -name "*.rlib"`; do
|
|
||||||
"${NM}" --format=posix -g "$dep" 2>/dev/null | sed 's/.*:.*//g' | awk '{if ($2 == "T") print $1}' | sed 's/^\(.*\)$/\1 __relibc_\1/g' >> $symbols_file
|
|
||||||
done
|
|
||||||
|
|
||||||
for special_sym in "${special_syms[@]}"; do
|
|
||||||
echo "$special_sym __relibc_$special_sym" >> $symbols_file
|
|
||||||
done
|
|
||||||
|
|
||||||
mangled_alloc_syms=$("${NM}" --format=posix -g "$target" 2>/dev/null | awk '{print $1}' | grep "7___rustc" || true)
|
|
||||||
for sym in $mangled_alloc_syms; do
|
|
||||||
echo "$sym __relibc_$sym" >> $symbols_file
|
|
||||||
done
|
|
||||||
|
|
||||||
sorted_file=`mktemp`
|
|
||||||
sort -u "$symbols_file" > "$sorted_file"
|
|
||||||
rm -f "$symbols_file"
|
|
||||||
|
|
||||||
"${OBJCOPY}" --redefine-syms="$sorted_file" "$target"
|
|
||||||
|
|
||||||
rm -f "$sorted_file"
|
|
||||||
+372
@@ -0,0 +1,372 @@
|
|||||||
|
# Automatically generated by update.sh
|
||||||
|
|
||||||
|
include = []
|
||||||
|
|
||||||
|
[general]
|
||||||
|
prompt = false
|
||||||
|
filesystem_size = 196
|
||||||
|
|
||||||
|
[packages.base]
|
||||||
|
|
||||||
|
[packages.bootloader]
|
||||||
|
|
||||||
|
[packages.ca-certificates]
|
||||||
|
|
||||||
|
[packages.coreutils]
|
||||||
|
|
||||||
|
[packages.extrautils]
|
||||||
|
|
||||||
|
[packages.ion]
|
||||||
|
|
||||||
|
[packages.kernel]
|
||||||
|
|
||||||
|
[packages.kibi]
|
||||||
|
|
||||||
|
[packages.libgcc]
|
||||||
|
|
||||||
|
[packages.libstdcxx]
|
||||||
|
|
||||||
|
[packages.netdb]
|
||||||
|
|
||||||
|
[packages.netutils]
|
||||||
|
|
||||||
|
[packages.pkgutils]
|
||||||
|
|
||||||
|
[packages.relibc]
|
||||||
|
|
||||||
|
[packages.userutils]
|
||||||
|
|
||||||
|
[packages.uutils]
|
||||||
|
|
||||||
|
[packages.uutils-procps]
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/login_schemes.toml"
|
||||||
|
data = """
|
||||||
|
[user_schemes.root]
|
||||||
|
schemes = ["*"]
|
||||||
|
[user_schemes.user]
|
||||||
|
schemes = [
|
||||||
|
# Kernel schemes
|
||||||
|
"debug",
|
||||||
|
"event",
|
||||||
|
"memory",
|
||||||
|
"pipe",
|
||||||
|
"serio",
|
||||||
|
"irq",
|
||||||
|
"time",
|
||||||
|
"sys",
|
||||||
|
# Base schemes
|
||||||
|
"rand",
|
||||||
|
"null",
|
||||||
|
"zero",
|
||||||
|
"log",
|
||||||
|
# Network schemes
|
||||||
|
"ip",
|
||||||
|
"icmp",
|
||||||
|
"tcp",
|
||||||
|
"udp",
|
||||||
|
# IPC schemes
|
||||||
|
"shm",
|
||||||
|
"chan",
|
||||||
|
"uds_stream",
|
||||||
|
"uds_dgram",
|
||||||
|
# File schemes
|
||||||
|
"file",
|
||||||
|
# Display schemes
|
||||||
|
"display.vesa",
|
||||||
|
"display*",
|
||||||
|
# Other schemes
|
||||||
|
"proc",
|
||||||
|
"pty",
|
||||||
|
"sudo",
|
||||||
|
"audio",
|
||||||
|
"orbital",
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/hostname"
|
||||||
|
data = "redox"
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/dns"
|
||||||
|
data = """
|
||||||
|
9.9.9.9
|
||||||
|
"""
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/ip"
|
||||||
|
data = """
|
||||||
|
10.0.2.15
|
||||||
|
"""
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/ip_router"
|
||||||
|
data = """
|
||||||
|
10.0.2.2
|
||||||
|
"""
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/net/ip_subnet"
|
||||||
|
data = """
|
||||||
|
255.255.255.0
|
||||||
|
"""
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/os-release"
|
||||||
|
data = """
|
||||||
|
PRETTY_NAME="Redox OS 0.9.0"
|
||||||
|
NAME="Redox OS"
|
||||||
|
VERSION_ID="0.9.0"
|
||||||
|
VERSION="0.9.0"
|
||||||
|
ID="redox-os"
|
||||||
|
|
||||||
|
HOME_URL="https://redox-os.org/"
|
||||||
|
DOCUMENTATION_URL="https://redox-os.org/docs/"
|
||||||
|
SUPPORT_URL="https://redox-os.org/community/"
|
||||||
|
"""
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/os-release"
|
||||||
|
data = "../usr/lib/os-release"
|
||||||
|
symlink = true
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/pkg.d/50_redox"
|
||||||
|
data = "https://static.redox-os.org/pkg"
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/bin"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/bin"
|
||||||
|
data = "usr/bin"
|
||||||
|
symlink = true
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/include"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/include"
|
||||||
|
data = "usr/include"
|
||||||
|
symlink = true
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/lib"
|
||||||
|
data = "usr/lib"
|
||||||
|
symlink = true
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/libexec"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/share"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/share"
|
||||||
|
data = "usr/share"
|
||||||
|
symlink = true
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/ui"
|
||||||
|
data = "usr/share/ui"
|
||||||
|
symlink = true
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/share/ui/fonts"
|
||||||
|
data = "/usr/share/fonts"
|
||||||
|
symlink = true
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/share/ui/icons"
|
||||||
|
data = "/usr/share/icons"
|
||||||
|
symlink = true
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/var"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/var/cache"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/var/lib"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/var/lock"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 1023
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/var/log"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/var/run"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 493
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/var/tmp"
|
||||||
|
data = ""
|
||||||
|
symlink = false
|
||||||
|
directory = true
|
||||||
|
mode = 1023
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/30_console"
|
||||||
|
data = """
|
||||||
|
requires_weak 10_net.target
|
||||||
|
inputd -A 2
|
||||||
|
nowait getty 2
|
||||||
|
nowait getty /scheme/debug/no-preserve -J
|
||||||
|
"""
|
||||||
|
symlink = false
|
||||||
|
directory = false
|
||||||
|
recursive_chown = false
|
||||||
|
postinstall = false
|
||||||
|
|
||||||
|
[users.root]
|
||||||
|
password = "password"
|
||||||
|
uid = 0
|
||||||
|
gid = 0
|
||||||
|
shell = "/usr/bin/ion"
|
||||||
|
|
||||||
|
[users.user]
|
||||||
|
password = ""
|
||||||
|
shell = "/usr/bin/ion"
|
||||||
|
|
||||||
|
[groups.sudo]
|
||||||
|
gid = 1
|
||||||
|
members = ["user"]
|
||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RES_PATH="$(dirname "$0")"
|
||||||
|
|
||||||
|
if [ -d "$1" ]
|
||||||
|
then
|
||||||
|
REDOX_PATH="$1"
|
||||||
|
else
|
||||||
|
echo "$0 [path to redox repository]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Update res/test.toml from the redoxer.toml template
|
||||||
|
"${REDOX_PATH}/build/fstools/bin/redox_installer" \
|
||||||
|
--config="${REDOX_PATH}/config/minimal.toml" \
|
||||||
|
--output-config="${RES_PATH}/test.toml"
|
||||||
|
sed -i '1s/^/# Automatically generated by update.sh\n\n/' "${RES_PATH}/test.toml"
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "nightly-2026-05-24"
|
|
||||||
components = ["rust-src"]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
blank_lines_lower_bound = 0
|
|
||||||
blank_lines_upper_bound = 1
|
|
||||||
brace_style = "SameLineWhere"
|
|
||||||
disable_all_formatting = false
|
|
||||||
edition = "2024"
|
|
||||||
empty_item_single_line = true
|
|
||||||
fn_single_line = false
|
|
||||||
force_explicit_abi = true
|
|
||||||
format_strings = false
|
|
||||||
hard_tabs = false
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
imports_indent = "Block"
|
|
||||||
imports_layout = "Mixed"
|
|
||||||
indent_style = "Block"
|
|
||||||
max_width = 100
|
|
||||||
newline_style = "Unix"
|
|
||||||
show_parse_errors = true
|
|
||||||
skip_children = false
|
|
||||||
tab_spaces = 4
|
|
||||||
trailing_comma = "Vertical"
|
|
||||||
where_single_line = false
|
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
extern crate arg_parser;
|
||||||
|
extern crate redox_installer;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate toml;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::{env, fs, process};
|
||||||
|
|
||||||
|
use arg_parser::ArgParser;
|
||||||
|
use pkg::net_backend::DownloadBackend;
|
||||||
|
|
||||||
|
use redox_installer::{Config, PackageConfig};
|
||||||
|
|
||||||
|
const HELP_STR: &str = r#"
|
||||||
|
redox_installer - Redox Installer.
|
||||||
|
Refer to link below for filesystem config reference:
|
||||||
|
https://doc.redox-os.org/book/configuration-settings.html
|
||||||
|
|
||||||
|
Using redox_installer as an installer:
|
||||||
|
redox_installer <diskpath.img> [--config=file.toml] [--write-bootloader=file.img] [--live] [--no-mount] [--skip-partition]
|
||||||
|
<diskpath.img> Disk file to write
|
||||||
|
--config Path to filesystem config TOML
|
||||||
|
--write-bootloader Path to write UEFI bootloader to in addition to the embedded ESP
|
||||||
|
--skip-partition Skip writing GPT partition tables
|
||||||
|
Use this only if you plan to use other partition tool
|
||||||
|
--live Use bootloader configured for live disk
|
||||||
|
--no-mount Use RedoxFS AR instead of FUSE to write files
|
||||||
|
--cookbook Use local Redox OS build system rather than downloading packages
|
||||||
|
--config-name Name of the filesystem configuration used for os-release VARIANT
|
||||||
|
|
||||||
|
Using redox_installer as a configuration parser:
|
||||||
|
redox_installer --config=file.toml [--list-packages|--filesystem-size|--output-config path]
|
||||||
|
--list-packages List packages will be installed
|
||||||
|
--filesystem-size Output filesystem size in MB
|
||||||
|
--output-config Path to write the parsed config as another TOML
|
||||||
|
"#;
|
||||||
|
|
||||||
|
fn os_release_quote(value: &str) -> String {
|
||||||
|
let escaped = value.replace('\\', "\\\\").replace('"', "\\\"");
|
||||||
|
format!("\"{}\"", escaped)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_id_from_repo_toml(repo_toml: &str) -> Option<String> {
|
||||||
|
toml::from_str::<toml::Value>(repo_toml)
|
||||||
|
.ok()?
|
||||||
|
.get("build_id")?
|
||||||
|
.as_str()
|
||||||
|
.and_then(|value| {
|
||||||
|
if value.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(value.to_string())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_repo_build_id(cookbook: &str) -> Option<String> {
|
||||||
|
let repo_toml = Path::new(cookbook)
|
||||||
|
.join("repo")
|
||||||
|
.join(redox_installer::get_target())
|
||||||
|
.join("repo.toml");
|
||||||
|
let repo_toml = fs::read_to_string(repo_toml).ok()?;
|
||||||
|
build_id_from_repo_toml(&repo_toml)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remote_repo_build_id() -> Option<String> {
|
||||||
|
let callback = Rc::new(RefCell::new(pkg::callback::SilentCallback::new()));
|
||||||
|
let download_backend = pkg::net_backend::DefaultNetBackend::new().ok()?;
|
||||||
|
let mut repo = pkg::RepoManager::new(callback, Box::new(download_backend));
|
||||||
|
repo.add_remote(
|
||||||
|
"https://static.redox-os.org/pkg",
|
||||||
|
&redox_installer::get_target(),
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
let package = pkg::PackageName::new("repo").ok()?;
|
||||||
|
let (repo_toml, _) = repo.get_package_toml(&package).ok()?;
|
||||||
|
build_id_from_repo_toml(&repo_toml)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_os_release_metadata(
|
||||||
|
config: &mut Config,
|
||||||
|
config_name: Option<&str>,
|
||||||
|
build_id: Option<String>,
|
||||||
|
) {
|
||||||
|
let mut data = String::new();
|
||||||
|
if let Some(config_name) = config_name.filter(|value| !value.is_empty()) {
|
||||||
|
data.push_str("VARIANT=");
|
||||||
|
data.push_str(&os_release_quote(config_name));
|
||||||
|
data.push('\n');
|
||||||
|
}
|
||||||
|
if let Some(build_id) = build_id.filter(|value| !value.is_empty()) {
|
||||||
|
data.push_str("BUILD_ID=");
|
||||||
|
data.push_str(&os_release_quote(&build_id));
|
||||||
|
data.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if !data.is_empty() {
|
||||||
|
config.files.push(redox_installer::FileConfig {
|
||||||
|
path: "/usr/lib/os-release".to_string(),
|
||||||
|
data,
|
||||||
|
append: true,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut parser = ArgParser::new(4)
|
||||||
|
.add_opt("b", "cookbook")
|
||||||
|
.add_opt("", "config-name")
|
||||||
|
.add_opt("c", "config")
|
||||||
|
.add_opt("o", "output-config")
|
||||||
|
.add_opt("", "write-bootloader")
|
||||||
|
.add_flag(&["skip-partition"])
|
||||||
|
.add_flag(&["filesystem-size"])
|
||||||
|
.add_flag(&["r", "repo-binary"]) // TODO: Remove
|
||||||
|
.add_flag(&["l", "list-packages"])
|
||||||
|
.add_flag(&["live"])
|
||||||
|
.add_flag(&["no-mount"]);
|
||||||
|
parser.parse(env::args());
|
||||||
|
|
||||||
|
let skip_partition = parser.found("skip-partition");
|
||||||
|
|
||||||
|
let mut config = if let Some(path) = parser.get_opt("config") {
|
||||||
|
match Config::from_file(Path::new(&path)) {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("installer: {err}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
redox_installer::Config::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get toml of merged config
|
||||||
|
let merged_toml = toml::to_string_pretty(&config).unwrap();
|
||||||
|
|
||||||
|
// Just output merged config and exit
|
||||||
|
if let Some(path) = parser.get_opt("output-config") {
|
||||||
|
fs::write(path, merged_toml).unwrap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add filesystem.toml to config
|
||||||
|
config.files.push(redox_installer::FileConfig {
|
||||||
|
path: "filesystem.toml".to_string(),
|
||||||
|
data: merged_toml,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
if skip_partition {
|
||||||
|
config.general.skip_partitions = Some(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if parser.found("filesystem-size") {
|
||||||
|
// An explicit `filesystem_size` in the config is an override and is
|
||||||
|
// honored as-is; otherwise the size is COMPUTED from the actual package
|
||||||
|
// set (see redox_installer::compute_filesystem_size_mb) — never guessed.
|
||||||
|
let size = match config.general.filesystem_size {
|
||||||
|
Some(size) => u64::from(size),
|
||||||
|
None => {
|
||||||
|
let cookbook = parser.get_opt("cookbook").unwrap_or_else(|| ".".to_string());
|
||||||
|
match redox_installer::compute_filesystem_size_mb(&config, &cookbook) {
|
||||||
|
Ok(mb) => mb,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("installer: filesystem-size: {err}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("{size}");
|
||||||
|
} else if parser.found("list-packages") {
|
||||||
|
// List the packages that should be fetched or built by the cookbook
|
||||||
|
for (packagename, package) in &config.packages {
|
||||||
|
match package {
|
||||||
|
PackageConfig::Build(rule) if rule == "ignore" => {
|
||||||
|
// skip this package
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("{}", packagename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let cookbook = if let Some(path) = parser.get_opt("cookbook") {
|
||||||
|
if !Path::new(&path).is_dir() {
|
||||||
|
eprintln!("installer: {}: cookbook not found", path);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let build_id = if let Some(cookbook) = cookbook.as_deref() {
|
||||||
|
local_repo_build_id(cookbook)
|
||||||
|
} else {
|
||||||
|
remote_repo_build_id()
|
||||||
|
};
|
||||||
|
append_os_release_metadata(
|
||||||
|
&mut config,
|
||||||
|
parser.get_opt("config-name").as_deref(),
|
||||||
|
build_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if cookbook.is_some() {
|
||||||
|
config.general.cookbook = cookbook;
|
||||||
|
}
|
||||||
|
if parser.found("live") {
|
||||||
|
config.general.live_disk = Some(true);
|
||||||
|
}
|
||||||
|
if parser.found("no-mount") {
|
||||||
|
config.general.no_mount = Some(true);
|
||||||
|
}
|
||||||
|
let write_bootloader = parser.get_opt("write-bootloader");
|
||||||
|
if write_bootloader.is_some() {
|
||||||
|
config.general.write_bootloader = write_bootloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(path) = parser.args.first() {
|
||||||
|
if let Err(err) = redox_installer::install(config, path) {
|
||||||
|
eprintln!("installer: failed to install: {:?}", err);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprint!("{}", HELP_STR);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use pkgar::{ext::EntryExt, PackageHead};
|
||||||
|
use pkgar_core::PackageSrc;
|
||||||
|
use pkgar_keys::PublicKeyFile;
|
||||||
|
use redox_installer::{try_fast_install, with_redoxfs_mount, with_whole_disk, Config, DiskOption};
|
||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
fs,
|
||||||
|
io::{self, Read, Write},
|
||||||
|
os::unix::fs::{symlink, MetadataExt, OpenOptionsExt},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: This is not the TUI a regular user would expect it does
|
||||||
|
// 1. Linux: Implement disk listing, use "dd" to write into whole disk
|
||||||
|
// 2. Allow partitioning to allow dual boot, possibly an integration with systemd-boot/grub
|
||||||
|
// 3. Prompt everything (disk password, users, preconfigured packages, import from existing img)
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
fn disk_paths(_paths: &mut Vec<(PathBuf, u64)>) {}
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
fn disk_paths(paths: &mut Vec<(PathBuf, u64)>) {
|
||||||
|
let mut schemes = Vec::new();
|
||||||
|
match fs::read_dir("/scheme") {
|
||||||
|
Ok(entries) => {
|
||||||
|
for entry_res in entries {
|
||||||
|
if let Ok(entry) = entry_res {
|
||||||
|
if let Ok(file_name) = entry.file_name().into_string() {
|
||||||
|
if file_name.starts_with("disk") {
|
||||||
|
schemes.push(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("redox_installer_tui: failed to list schemes: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for scheme in schemes {
|
||||||
|
if scheme.is_dir() {
|
||||||
|
match fs::read_dir(&scheme) {
|
||||||
|
Ok(entries) => {
|
||||||
|
for entry_res in entries {
|
||||||
|
if let Ok(entry) = entry_res {
|
||||||
|
if let Ok(file_name) = entry.file_name().into_string() {
|
||||||
|
if file_name.contains('p') {
|
||||||
|
// Skip partitions
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(metadata) = entry.metadata() {
|
||||||
|
let size = metadata.len();
|
||||||
|
if size > 0 {
|
||||||
|
paths.push((entry.path(), size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redox_installer_tui: failed to list '{}': {}",
|
||||||
|
scheme.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<()> {
|
||||||
|
if let Some(parent) = dest.parent() {
|
||||||
|
// Parent may be a symlink
|
||||||
|
if !parent.is_symlink() {
|
||||||
|
match fs::create_dir_all(&parent) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to create directory {}: {}", parent.display(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let metadata = match fs::symlink_metadata(&src) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to read metadata of {}: {}", src.display(), err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if metadata.file_type().is_symlink() {
|
||||||
|
let real_src = match fs::read_link(&src) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to read link {}: {}", src.display(), err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match symlink(&real_src, &dest) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => {
|
||||||
|
bail!(
|
||||||
|
"failed to copy link {} ({}) to {}: {}",
|
||||||
|
src.display(),
|
||||||
|
real_src.display(),
|
||||||
|
dest.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut src_file = match fs::File::open(&src) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to open file {}: {}", src.display(), err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut dest_file = match fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.mode(metadata.mode())
|
||||||
|
.open(&dest)
|
||||||
|
{
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to create file {}: {}", dest.display(), err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let count = match src_file.read(buf) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to read file {}: {}", src.display(), err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match dest_file.write_all(&buf[..count]) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to write file {}: {}", dest.display(), err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn package_files(
|
||||||
|
root_path: &Path,
|
||||||
|
config: &mut Config,
|
||||||
|
files: &mut Vec<String>,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
//TODO: Remove packages from config where all files are located (and have valid shasum?)
|
||||||
|
config.packages.clear();
|
||||||
|
|
||||||
|
let pkey_path = "pkg/id_ed25519.pub.toml";
|
||||||
|
let pkey = PublicKeyFile::open(&root_path.join(pkey_path))?.pkey;
|
||||||
|
files.push(pkey_path.to_string());
|
||||||
|
|
||||||
|
for item_res in fs::read_dir(&root_path.join("pkg"))? {
|
||||||
|
let item = item_res?;
|
||||||
|
let pkg_path = item.path();
|
||||||
|
if pkg_path.extension() == Some(OsStr::new("pkgar_head")) {
|
||||||
|
let mut pkg = PackageHead::new(&pkg_path, &root_path, &pkey)?;
|
||||||
|
for entry in pkg.read_entries()? {
|
||||||
|
files.push(entry.check_path()?.to_str().unwrap().to_string());
|
||||||
|
}
|
||||||
|
files.push(
|
||||||
|
pkg_path
|
||||||
|
.strip_prefix(root_path)
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose_disk() -> PathBuf {
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
disk_paths(&mut paths);
|
||||||
|
loop {
|
||||||
|
for (i, (path, size)) in paths.iter().enumerate() {
|
||||||
|
eprintln!(
|
||||||
|
"\x1B[1m{}\x1B[0m: {}: {}",
|
||||||
|
i + 1,
|
||||||
|
path.display(),
|
||||||
|
redox_installer::format_bytes(*size)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if paths.is_empty() {
|
||||||
|
eprintln!("redox_installer_tui: no RedoxFS partition found");
|
||||||
|
eprintln!("redox_installer_tui: this tool is used to overwrite unmounted RedoxFS disk in Redox OS");
|
||||||
|
process::exit(1);
|
||||||
|
} else {
|
||||||
|
eprint!("Select a drive from 1 to {}: ", paths.len());
|
||||||
|
|
||||||
|
let mut line = String::new();
|
||||||
|
match io::stdin().read_line(&mut line) {
|
||||||
|
Ok(0) => {
|
||||||
|
eprintln!("redox_installer_tui: failed to read line: end of input");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("redox_installer_tui: failed to read line: {}", err);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match line.trim().parse::<usize>() {
|
||||||
|
Ok(i) => {
|
||||||
|
if i >= 1 && i <= paths.len() {
|
||||||
|
break paths[i - 1].0.clone();
|
||||||
|
} else {
|
||||||
|
eprintln!("{} not from 1 to {}", i, paths.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("invalid input: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let root_path = Path::new("/");
|
||||||
|
|
||||||
|
let disk_path = choose_disk();
|
||||||
|
|
||||||
|
let Ok(password_opt) = redox_installer::prompt_password(
|
||||||
|
"redox_installer_tui: redoxfs password (empty for none)",
|
||||||
|
"redox_installer_tui: confirm password",
|
||||||
|
) else {
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let instant = std::time::Instant::now();
|
||||||
|
|
||||||
|
let bootloader_bios = {
|
||||||
|
let path = root_path.join("usr/lib/boot/bootloader.bios");
|
||||||
|
if path.exists() {
|
||||||
|
match fs::read(&path) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redox_installer_tui: {}: failed to read: {}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let bootloader_efi = {
|
||||||
|
let path = root_path.join("usr/lib/boot/bootloader.efi");
|
||||||
|
if path.exists() {
|
||||||
|
match fs::read(&path) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redox_installer_tui: {}: failed to read: {}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let disk_option = DiskOption {
|
||||||
|
bootloader_bios: &bootloader_bios,
|
||||||
|
bootloader_efi: &bootloader_efi,
|
||||||
|
password_opt: password_opt.as_ref().map(|x| x.as_bytes()),
|
||||||
|
efi_partition_size: None,
|
||||||
|
skip_partitions: false, // TODO?
|
||||||
|
};
|
||||||
|
let res = with_whole_disk(&disk_path, &disk_option, |mut fs| {
|
||||||
|
// Fast install method via filesystem clone
|
||||||
|
let mut last_percent = 0;
|
||||||
|
if try_fast_install(&mut fs, move |used, used_old| {
|
||||||
|
let percent = (used * 100) / used_old;
|
||||||
|
if percent != last_percent {
|
||||||
|
eprint!(
|
||||||
|
"\r{}%: {} MB/{} MB",
|
||||||
|
percent,
|
||||||
|
used / 1000 / 1000,
|
||||||
|
used_old / 1000 / 1000
|
||||||
|
);
|
||||||
|
last_percent = percent;
|
||||||
|
}
|
||||||
|
})? {
|
||||||
|
eprintln!("\rfinished installing using fast mode");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow install method via file copy
|
||||||
|
with_redoxfs_mount(fs, None, |mount_path| {
|
||||||
|
let mut config: Config = Config::from_file(&root_path.join("filesystem.toml"))?;
|
||||||
|
|
||||||
|
// Copy filesystem.toml, which is not packaged
|
||||||
|
let mut files = vec!["filesystem.toml".to_string()];
|
||||||
|
|
||||||
|
// Copy files from locally installed packages
|
||||||
|
package_files(&root_path, &mut config, &mut files)
|
||||||
|
// TODO: implement Error trait
|
||||||
|
.map_err(|err| anyhow!("failed to read package files: {err}"))?;
|
||||||
|
|
||||||
|
// Perform config install (after packages have been converted to files)
|
||||||
|
eprintln!("configuring system");
|
||||||
|
let cookbook: Option<&'static str> = None;
|
||||||
|
redox_installer::install_dir(config, mount_path, cookbook)
|
||||||
|
.map_err(|err| io::Error::other(err))?;
|
||||||
|
|
||||||
|
// Sort and remove duplicates
|
||||||
|
files.sort();
|
||||||
|
files.dedup();
|
||||||
|
|
||||||
|
// Install files
|
||||||
|
let mut buf = vec![0; 4096 * 1024];
|
||||||
|
for (i, name) in files.iter().enumerate() {
|
||||||
|
eprintln!("copy {} [{}/{}]", name, i, files.len());
|
||||||
|
|
||||||
|
let src = root_path.join(name);
|
||||||
|
let dest = mount_path.join(name);
|
||||||
|
copy_file(&src, &dest, &mut buf)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("finished installing, unmounting filesystem");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(()) => {
|
||||||
|
eprintln!(
|
||||||
|
"redox_installer_tui: installed successfully in {:?}",
|
||||||
|
instant.elapsed()
|
||||||
|
);
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("redox_installer_tui: failed to install: {:?}", err);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use crate::platform::types::c_char;
|
|
||||||
|
|
||||||
/// An abstraction over a byte literal to provide a method to convert safely
|
|
||||||
/// to a `c_char`.
|
|
||||||
///
|
|
||||||
/// The abstraction is required so we can contain architecture specific code
|
|
||||||
/// in a central location.
|
|
||||||
pub struct ByteLiteral;
|
|
||||||
|
|
||||||
impl ByteLiteral {
|
|
||||||
/// Casts a byte literal (`u8`) to a `c_char` without using `as`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// If `input` is not within the following range of ascii characters:
|
|
||||||
/// - Octal: `040`..=`176`
|
|
||||||
/// - Decimal: `30`..=`126`
|
|
||||||
/// - Hexadecimal: `20`..=`7E`
|
|
||||||
/// - Byte literals: The space character (` `) upto and including tilde (`~`)
|
|
||||||
pub const fn cast_cchar(input: u8) -> c_char {
|
|
||||||
match input {
|
|
||||||
b' '..=b'~' => {
|
|
||||||
// `c_char` is an `i8` on these arches
|
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
|
|
||||||
{
|
|
||||||
input.cast_signed()
|
|
||||||
}
|
|
||||||
// `c_char` is already a `u8` on these arches
|
|
||||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
|
||||||
{
|
|
||||||
input
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => panic!("Not a printable ascii character!"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
double strtod(const char *nptr, char **endptr);
|
|
||||||
|
|
||||||
long double strtold(const char *nptr, char **endptr) {
|
|
||||||
return (long double)strtod(nptr, endptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
double relibc_ldtod(const long double* val) {
|
|
||||||
return (double)(*val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void relibc_dtold(double val, long double* out) {
|
|
||||||
*out = (long double)val;
|
|
||||||
}
|
|
||||||
-326
@@ -1,326 +0,0 @@
|
|||||||
//! Nul-terminated byte strings.
|
|
||||||
|
|
||||||
use core::{marker::PhantomData, ptr::NonNull, str::Utf8Error};
|
|
||||||
|
|
||||||
use alloc::{borrow::Cow, string::String};
|
|
||||||
|
|
||||||
use crate::platform::types::{c_char, wchar_t};
|
|
||||||
|
|
||||||
mod private {
|
|
||||||
pub trait Sealed {}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum Thin {}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum Wide {}
|
|
||||||
|
|
||||||
impl private::Sealed for Thin {}
|
|
||||||
impl private::Sealed for Wide {}
|
|
||||||
|
|
||||||
pub trait Kind: private::Sealed + Copy + 'static {
|
|
||||||
/// c_char or wchar_t
|
|
||||||
type C: Copy + 'static;
|
|
||||||
// u8 or u32
|
|
||||||
type Char: Copy + From<u8> + Into<u32> + PartialEq + 'static;
|
|
||||||
|
|
||||||
const NUL: Self::Char;
|
|
||||||
|
|
||||||
const IS_THIN_NOT_WIDE: bool;
|
|
||||||
|
|
||||||
fn r2c(c: Self::Char) -> Self::C;
|
|
||||||
fn c2r(c: Self::C) -> Self::Char;
|
|
||||||
|
|
||||||
fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]>;
|
|
||||||
fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]>;
|
|
||||||
|
|
||||||
unsafe fn strlen(s: *const Self::C) -> usize;
|
|
||||||
unsafe fn strchr(s: *const Self::C, c: Self::C) -> *const Self::C;
|
|
||||||
unsafe fn strchrnul(s: *const Self::C, c: Self::C) -> *const Self::C;
|
|
||||||
}
|
|
||||||
impl Kind for Thin {
|
|
||||||
type C = c_char;
|
|
||||||
type Char = u8;
|
|
||||||
|
|
||||||
const NUL: Self::Char = 0;
|
|
||||||
const IS_THIN_NOT_WIDE: bool = true;
|
|
||||||
|
|
||||||
unsafe fn strlen(s: *const c_char) -> usize {
|
|
||||||
unsafe { crate::header::string::strlen(s) }
|
|
||||||
}
|
|
||||||
unsafe fn strchr(s: *const c_char, c: c_char) -> *const c_char {
|
|
||||||
unsafe { crate::header::string::strchr(s, c.into()) }
|
|
||||||
}
|
|
||||||
unsafe fn strchrnul(s: *const c_char, c: c_char) -> *const c_char {
|
|
||||||
unsafe { crate::header::string::strchrnul(s, c.into()) }
|
|
||||||
}
|
|
||||||
fn r2c(c: u8) -> c_char {
|
|
||||||
c as _
|
|
||||||
}
|
|
||||||
fn c2r(c: c_char) -> u8 {
|
|
||||||
c as _
|
|
||||||
}
|
|
||||||
fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]> {
|
|
||||||
Some(b)
|
|
||||||
}
|
|
||||||
fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]> {
|
|
||||||
Some(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Kind for Wide {
|
|
||||||
type C = wchar_t;
|
|
||||||
type Char = u32;
|
|
||||||
|
|
||||||
const NUL: Self::Char = 0;
|
|
||||||
const IS_THIN_NOT_WIDE: bool = false;
|
|
||||||
|
|
||||||
unsafe fn strlen(s: *const Self::C) -> usize {
|
|
||||||
unsafe { crate::header::wchar::wcslen(s) }
|
|
||||||
}
|
|
||||||
unsafe fn strchr(s: *const Self::C, c: Self::C) -> *const Self::C {
|
|
||||||
unsafe { crate::header::wchar::wcschr(s, c) }
|
|
||||||
}
|
|
||||||
unsafe fn strchrnul(mut s: *const Self::C, c: Self::C) -> *const Self::C {
|
|
||||||
// TODO: optimized function
|
|
||||||
while unsafe { s.read() } != c && unsafe { s.read() } != 0 {
|
|
||||||
s = unsafe { s.add(1) };
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
fn r2c(c: Self::Char) -> Self::C {
|
|
||||||
c as _
|
|
||||||
}
|
|
||||||
fn c2r(c: Self::C) -> Self::Char {
|
|
||||||
c as _
|
|
||||||
}
|
|
||||||
fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Safe wrapper for immutable borrowed C strings, guaranteed to be the same layout as `*const u8`.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct NulStr<'a, T: Kind> {
|
|
||||||
ptr: NonNull<T::C>,
|
|
||||||
_marker: PhantomData<&'a [u8]>,
|
|
||||||
}
|
|
||||||
pub type CStr<'a> = NulStr<'a, Thin>;
|
|
||||||
pub type WStr<'a> = NulStr<'a, Wide>;
|
|
||||||
|
|
||||||
impl<'a, T: Kind> NulStr<'a, T> {
|
|
||||||
/// Safety
|
|
||||||
///
|
|
||||||
/// The ptr must be valid up to and including the first NUL byte from the base ptr.
|
|
||||||
pub const unsafe fn from_ptr(ptr: *const T::C) -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: unsafe { NonNull::new_unchecked(ptr.cast_mut()) },
|
|
||||||
_marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub unsafe fn from_nullable_ptr(ptr: *const T::C) -> Option<Self> {
|
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(unsafe { Self::from_ptr(ptr) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Look for the closest occurence of `c`, and if found, split the string into a slice up to
|
|
||||||
/// that byte and a `CStr` starting at that byte.
|
|
||||||
#[inline]
|
|
||||||
#[doc(alias = "strchrnul")]
|
|
||||||
pub fn find_get_subslice_or_all(
|
|
||||||
self,
|
|
||||||
c: impl Into<T::Char>,
|
|
||||||
) -> Result<(&'a [T::Char], Self), (&'a [T::Char], Self)> {
|
|
||||||
let c = c.into();
|
|
||||||
|
|
||||||
// SAFETY: strchrnul expects self.as_ptr() to be valid up to and including its last NUL
|
|
||||||
// byte
|
|
||||||
let found = unsafe { T::strchrnul(self.as_ptr(), T::r2c(c)) };
|
|
||||||
|
|
||||||
// SAFETY: the pointer returned from strchrnul is always a substring of this string, and
|
|
||||||
// hence always valid as a CStr.
|
|
||||||
let found = unsafe { Self::from_ptr(found) };
|
|
||||||
let until = unsafe { self.slice_until_substr(found) };
|
|
||||||
|
|
||||||
if found.first() == T::NUL {
|
|
||||||
// The character was not found, and we got the end of the string instead.
|
|
||||||
Err((until, found))
|
|
||||||
} else {
|
|
||||||
Ok((until, found))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `substr` must be contained within `self`
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn slice_until_substr(self, substr: NulStr<'_, T>) -> &'a [T::Char] {
|
|
||||||
let index = unsafe {
|
|
||||||
// SAFETY: the sub-pointer as returned by strchr must be derived from the same
|
|
||||||
// allocation
|
|
||||||
substr.as_ptr().offset_from(self.as_ptr()) as usize
|
|
||||||
};
|
|
||||||
unsafe { core::slice::from_raw_parts(self.as_ptr().cast::<T::Char>(), index) }
|
|
||||||
}
|
|
||||||
/// Look for the closest occurence of `c`, and if found, split the string into a slice up to
|
|
||||||
/// that byte and a `CStr` starting at that byte.
|
|
||||||
#[inline]
|
|
||||||
pub fn find_get_subslice(self, c: T::Char) -> Option<(&'a [T::Char], Self)> {
|
|
||||||
let rest = self.find(c)?;
|
|
||||||
|
|
||||||
// SAFETY: the output of strchr is obviously a substring if it doesn't return NULL
|
|
||||||
Some((unsafe { self.slice_until_substr(rest) }, rest))
|
|
||||||
}
|
|
||||||
/// Look for the closest occurence of `c`, and return a new string starting at that byte if
|
|
||||||
/// found.
|
|
||||||
#[doc(alias = "strchr")]
|
|
||||||
#[doc(alias = "wcschr")]
|
|
||||||
#[inline]
|
|
||||||
pub fn find(self, c: T::Char) -> Option<Self> {
|
|
||||||
unsafe {
|
|
||||||
// SAFETY: the only requirement is for self.as_ptr() to be valid up to and including
|
|
||||||
// the nearest NUL byte, which this type requires
|
|
||||||
let ret = T::strchr(self.as_ptr(), T::r2c(c));
|
|
||||||
// SAFETY: strchr must either return NULL (not found) or a substring of self, which can
|
|
||||||
// never exceed the nearest NUL byte of self
|
|
||||||
Self::from_nullable_ptr(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: strrchr, strchrnul wrappers
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn contains(self, c: T::Char) -> bool {
|
|
||||||
self.find(c).is_some()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn first(self) -> T::Char {
|
|
||||||
unsafe {
|
|
||||||
// SAFETY: Self must be valid up to and including its nearest NUL byte, which certainly
|
|
||||||
// implies its readable length is nonzero (string is empty if this first byte is 0).
|
|
||||||
T::c2r(self.ptr.read())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn first_char(self) -> Option<char> {
|
|
||||||
char::from_u32(self.first().into())
|
|
||||||
}
|
|
||||||
/// Same as `split_first` except also requires that the first char be convertible into `char`
|
|
||||||
#[inline]
|
|
||||||
pub fn split_first_char(self) -> Option<(char, Self)> {
|
|
||||||
self.split_first()
|
|
||||||
.and_then(|(c, r)| Some((char::from_u32(c.into())?, r)))
|
|
||||||
}
|
|
||||||
/// Split this string into `Some((first_byte, string_after_that))` or `None` if empty.
|
|
||||||
#[inline]
|
|
||||||
pub fn split_first(self) -> Option<(T::Char, Self)> {
|
|
||||||
if self.first() == T::NUL {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some((self.first(), unsafe {
|
|
||||||
Self::from_ptr(self.as_ptr().add(1))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
pub fn to_chars_with_nul(self) -> &'a [T::Char] {
|
|
||||||
unsafe {
|
|
||||||
// SAFETY: The string must be valid at least until (and including) the NUL byte.
|
|
||||||
let len = T::strlen(self.ptr.as_ptr());
|
|
||||||
core::slice::from_raw_parts(self.ptr.as_ptr().cast(), len + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn to_chars(self) -> &'a [T::Char] {
|
|
||||||
let s = self.to_chars_with_nul();
|
|
||||||
&s[..s.len() - 1]
|
|
||||||
}
|
|
||||||
pub const fn as_ptr(self) -> *const T::C {
|
|
||||||
self.ptr.as_ptr()
|
|
||||||
}
|
|
||||||
pub const unsafe fn from_chars_with_nul_unchecked(chars: &'a [T::Char]) -> Self {
|
|
||||||
unsafe { Self::from_ptr(chars.as_ptr().cast()) }
|
|
||||||
}
|
|
||||||
pub fn from_chars_with_nul(chars: &'a [T::Char]) -> Result<Self, FromCharsWithNulError> {
|
|
||||||
if chars.last() != Some(&T::NUL) || chars[..chars.len() - 1].contains(&T::NUL) {
|
|
||||||
return Err(FromCharsWithNulError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(unsafe { Self::from_chars_with_nul_unchecked(chars) })
|
|
||||||
}
|
|
||||||
pub fn from_chars_until_nul(chars: &'a [T::Char]) -> Result<Self, FromCharsUntilNulError> {
|
|
||||||
if !chars.contains(&T::NUL) {
|
|
||||||
return Err(FromCharsUntilNulError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(unsafe { Self::from_chars_with_nul_unchecked(chars) })
|
|
||||||
}
|
|
||||||
/// Scan the string to get its length.
|
|
||||||
#[doc(alias = "strlen")]
|
|
||||||
#[doc(alias = "wcslen")]
|
|
||||||
#[expect(clippy::len_without_is_empty, reason = "len() signature intentional")]
|
|
||||||
pub fn len(self) -> usize {
|
|
||||||
self.to_chars().len()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.first() == T::NUL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> CStr<'a> {
|
|
||||||
pub fn to_owned_cstring(self) -> CString {
|
|
||||||
CString::from(self.to_cstr())
|
|
||||||
}
|
|
||||||
pub fn borrow(string: &'a CString) -> Self {
|
|
||||||
unsafe { Self::from_ptr(string.as_ptr()) }
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn to_bytes(self) -> &'a [u8] {
|
|
||||||
self.to_chars()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn to_bytes_with_nul(self) -> &'a [u8] {
|
|
||||||
self.to_chars_with_nul()
|
|
||||||
}
|
|
||||||
pub fn to_str(self) -> Result<&'a str, Utf8Error> {
|
|
||||||
core::str::from_utf8(self.to_bytes())
|
|
||||||
}
|
|
||||||
pub fn to_cstr(self) -> &'a core::ffi::CStr {
|
|
||||||
unsafe { core::ffi::CStr::from_ptr(self.ptr.as_ptr()) }
|
|
||||||
}
|
|
||||||
pub fn to_string_lossy(self) -> Cow<'a, str> {
|
|
||||||
String::from_utf8_lossy(self.to_bytes())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &'a [u8]) -> Self {
|
|
||||||
unsafe { Self::from_chars_with_nul_unchecked(bytes) }
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn from_bytes_with_nul(bytes: &'a [u8]) -> Result<Self, FromCharsWithNulError> {
|
|
||||||
Self::from_chars_with_nul(bytes)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn from_bytes_until_nul(bytes: &'a [u8]) -> Result<Self, FromCharsUntilNulError> {
|
|
||||||
Self::from_chars_until_nul(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Kind> Send for NulStr<'_, T> {}
|
|
||||||
unsafe impl<T: Kind> Sync for NulStr<'_, T> {}
|
|
||||||
|
|
||||||
impl From<&core::ffi::CStr> for CStr<'_> {
|
|
||||||
fn from(s: &core::ffi::CStr) -> Self {
|
|
||||||
// SAFETY:
|
|
||||||
// * We can assume that `s` is valid because the caller should have upheld its
|
|
||||||
// safety concerns when constructing it.
|
|
||||||
unsafe { Self::from_ptr(s.as_ptr()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FromCharsWithNulError;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FromCharsUntilNulError;
|
|
||||||
|
|
||||||
pub use alloc::ffi::CString;
|
|
||||||
-294
@@ -1,294 +0,0 @@
|
|||||||
//! Equivalent of Rust's `Vec<T>`, but using relibc's own allocator.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
io::{self, Write},
|
|
||||||
platform::{self, WriteByte, types::*},
|
|
||||||
};
|
|
||||||
use core::{
|
|
||||||
cmp, fmt,
|
|
||||||
iter::IntoIterator,
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
ptr::{self, NonNull},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Error that occurs when an allocation fails
|
|
||||||
#[derive(Debug, Default, Hash, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub struct AllocError;
|
|
||||||
|
|
||||||
/// A normal vector allocated in Rust needs to be dropped from Rust
|
|
||||||
/// too, in order to avoid UB. This CVec is an abstraction that works
|
|
||||||
/// using only C allocations functions and can therefore be dropped
|
|
||||||
/// from C. Just like the Rust Vec, this does bounds checks to assure
|
|
||||||
/// you never reach isize::MAX. Unless you need to drop something from
|
|
||||||
/// C, prefer Rust's builtin Vec.
|
|
||||||
pub struct CVec<T> {
|
|
||||||
ptr: NonNull<T>,
|
|
||||||
len: usize,
|
|
||||||
cap: usize,
|
|
||||||
}
|
|
||||||
impl<T> CVec<T> {
|
|
||||||
#[allow(clippy::new_without_default)]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: NonNull::dangling(),
|
|
||||||
len: 0,
|
|
||||||
cap: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn check_bounds(i: usize) -> Result<usize, AllocError> {
|
|
||||||
if i > isize::MAX as usize {
|
|
||||||
Err(AllocError)
|
|
||||||
} else {
|
|
||||||
Ok(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn check_mul(x: usize, y: usize) -> Result<usize, AllocError> {
|
|
||||||
x.checked_mul(y)
|
|
||||||
.ok_or(AllocError)
|
|
||||||
.and_then(Self::check_bounds)
|
|
||||||
}
|
|
||||||
pub fn with_capacity(cap: usize) -> Result<Self, AllocError> {
|
|
||||||
if cap == 0 {
|
|
||||||
return Ok(Self::new());
|
|
||||||
}
|
|
||||||
let size = Self::check_mul(cap, mem::size_of::<T>())?;
|
|
||||||
let ptr = NonNull::new(unsafe { platform::alloc(size).cast::<T>() }).ok_or(AllocError)?;
|
|
||||||
Ok(Self { ptr, len: 0, cap })
|
|
||||||
}
|
|
||||||
unsafe fn resize(&mut self, cap: usize) -> Result<(), AllocError> {
|
|
||||||
let size = Self::check_mul(cap, mem::size_of::<T>())?;
|
|
||||||
let ptr = if cap == 0 {
|
|
||||||
NonNull::dangling()
|
|
||||||
} else if self.cap > 0 {
|
|
||||||
NonNull::new(
|
|
||||||
unsafe { platform::realloc(self.ptr.as_ptr().cast::<c_void>(), size) }.cast::<T>(),
|
|
||||||
)
|
|
||||||
.ok_or(AllocError)?
|
|
||||||
} else {
|
|
||||||
NonNull::new((unsafe { platform::alloc(size) }).cast::<T>()).ok_or(AllocError)?
|
|
||||||
};
|
|
||||||
self.ptr = ptr;
|
|
||||||
self.cap = cap;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
unsafe fn drop_range(&mut self, start: usize, end: usize) {
|
|
||||||
let mut start = unsafe { self.ptr.as_ptr().add(start) };
|
|
||||||
let end = unsafe { self.ptr.as_ptr().add(end) };
|
|
||||||
while start < end {
|
|
||||||
unsafe { ptr::drop_in_place(start) };
|
|
||||||
start = unsafe { start.add(1) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push stuff
|
|
||||||
|
|
||||||
pub fn reserve(&mut self, required: usize) -> Result<(), AllocError> {
|
|
||||||
let required_len = self
|
|
||||||
.len
|
|
||||||
.checked_add(required)
|
|
||||||
.ok_or(AllocError)
|
|
||||||
.and_then(Self::check_bounds)?;
|
|
||||||
if required_len > self.cap {
|
|
||||||
let new_cap = cmp::min(required_len.next_power_of_two(), isize::MAX as usize);
|
|
||||||
unsafe {
|
|
||||||
self.resize(new_cap)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn push(&mut self, elem: T) -> Result<(), AllocError> {
|
|
||||||
self.reserve(1)?;
|
|
||||||
unsafe {
|
|
||||||
ptr::write(self.ptr.as_ptr().add(self.len), elem);
|
|
||||||
}
|
|
||||||
self.len += 1; // no need to bounds check, as new len <= cap
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn extend_from_slice(&mut self, elems: &[T]) -> Result<(), AllocError>
|
|
||||||
where
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
self.reserve(elems.len())?;
|
|
||||||
unsafe {
|
|
||||||
ptr::copy_nonoverlapping(elems.as_ptr(), self.ptr.as_ptr().add(self.len), elems.len());
|
|
||||||
}
|
|
||||||
self.len += elems.len(); // no need to bounds check, as new len <= cap
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn append(&mut self, other: &mut Self) -> Result<(), AllocError> {
|
|
||||||
let len = other.len;
|
|
||||||
other.len = 0; // move
|
|
||||||
self.reserve(len)?;
|
|
||||||
unsafe {
|
|
||||||
ptr::copy_nonoverlapping(other.as_ptr(), self.ptr.as_ptr().add(self.len), len);
|
|
||||||
}
|
|
||||||
self.len += other.len(); // no need to bounds check, as new len <= cap
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop stuff
|
|
||||||
|
|
||||||
pub fn truncate(&mut self, len: usize) {
|
|
||||||
if len < self.len {
|
|
||||||
unsafe {
|
|
||||||
let old_len = self.len;
|
|
||||||
self.drop_range(len, old_len);
|
|
||||||
}
|
|
||||||
self.len = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn shrink_to_fit(&mut self) -> Result<(), AllocError> {
|
|
||||||
if self.len < self.cap {
|
|
||||||
unsafe {
|
|
||||||
let new_cap = self.len;
|
|
||||||
self.resize(new_cap)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn pop(&mut self) -> Option<T> {
|
|
||||||
if self.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let elem = unsafe { ptr::read(self.as_ptr().add(self.len - 1)) };
|
|
||||||
self.len -= 1;
|
|
||||||
Some(elem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Misc stuff
|
|
||||||
|
|
||||||
pub fn capacity(&self) -> usize {
|
|
||||||
self.cap
|
|
||||||
}
|
|
||||||
pub fn as_ptr(&self) -> *const T {
|
|
||||||
self.ptr.as_ptr()
|
|
||||||
}
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut T {
|
|
||||||
self.ptr.as_ptr()
|
|
||||||
}
|
|
||||||
/// Leaks the inner data. This is safe to drop from C!
|
|
||||||
pub fn leak(mut self) -> *mut T {
|
|
||||||
let ptr = self.as_mut_ptr();
|
|
||||||
mem::forget(self);
|
|
||||||
ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Deref for CVec<T> {
|
|
||||||
type Target = [T];
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> DerefMut for CVec<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Drop for CVec<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
let len = self.len;
|
|
||||||
self.drop_range(0, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a, T> IntoIterator for &'a CVec<T> {
|
|
||||||
type Item = <&'a [T] as IntoIterator>::Item;
|
|
||||||
type IntoIter = <&'a [T] as IntoIterator>::IntoIter;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
<&[T]>::into_iter(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a, T> IntoIterator for &'a mut CVec<T> {
|
|
||||||
type Item = <&'a mut [T] as IntoIterator>::Item;
|
|
||||||
type IntoIter = <&'a mut [T] as IntoIterator>::IntoIter;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
<&mut [T]>::into_iter(&mut *self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for CVec<u8> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.extend_from_slice(buf).map_err(|err| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"AllocStringWriter::write failed to allocate",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl fmt::Write for CVec<u8> {
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
self.write(s.as_bytes()).map_err(|_| fmt::Error)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl WriteByte for CVec<u8> {
|
|
||||||
fn write_u8(&mut self, byte: u8) -> fmt::Result {
|
|
||||||
self.write(&[byte]).map_err(|_| fmt::Error)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::CVec;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_pop() {
|
|
||||||
let mut vec = CVec::new();
|
|
||||||
vec.push(1).unwrap();
|
|
||||||
vec.push(2).unwrap();
|
|
||||||
vec.push(3).unwrap();
|
|
||||||
assert_eq!(&vec[..], &[1, 2, 3]);
|
|
||||||
assert_eq!(vec.pop().unwrap(), 3);
|
|
||||||
assert_eq!(&vec[..], &[1, 2]);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn extend_from_slice() {
|
|
||||||
use crate::io::Write;
|
|
||||||
|
|
||||||
let mut vec = CVec::new();
|
|
||||||
vec.extend_from_slice(&[1, 2, 3]).unwrap();
|
|
||||||
vec.extend_from_slice(&[4, 5, 6]).unwrap();
|
|
||||||
assert_eq!(&vec[..], &[1, 2, 3, 4, 5, 6]);
|
|
||||||
assert_eq!(vec.write(&[7, 8, 9]).unwrap(), 3);
|
|
||||||
assert_eq!(&vec[..], &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn dropped() {
|
|
||||||
use alloc::rc::Rc;
|
|
||||||
|
|
||||||
let counter = Rc::new(());
|
|
||||||
let mut vec = CVec::with_capacity(3).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 4);
|
|
||||||
|
|
||||||
let popped = vec.pop().unwrap();
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 4);
|
|
||||||
drop(popped);
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 3);
|
|
||||||
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(vec.len(), 5);
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 6);
|
|
||||||
vec.truncate(1);
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 2);
|
|
||||||
|
|
||||||
drop(vec);
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,418 @@
|
|||||||
|
//! Runtime file-collision detection for `install_dir()`.
|
||||||
|
//!
|
||||||
|
//! The installer writes files in four layers (see AGENTS.md § "Installer File
|
||||||
|
//! Layering"):
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! Layer 1: Config pre-install [[files]] (postinstall = false)
|
||||||
|
//! Layer 2: Package staging (install_packages())
|
||||||
|
//! Layer 3: Config post-install [[files]] (postinstall = true)
|
||||||
|
//! Layer 4: User/group creation (passwd, shadow, group)
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Last writer wins. Layer 2 silently overwriting Layer 1 is the D-Bus
|
||||||
|
//! regression class: a config `[[files]]` entry writes to a path, a package
|
||||||
|
//! stages the same path during `install_packages()`, the package wins, the
|
||||||
|
//! operator's customization is gone — and the build succeeds. This module
|
||||||
|
//! makes that class of bug visible at install time.
|
||||||
|
//!
|
||||||
|
//! ## Behavior
|
||||||
|
//!
|
||||||
|
//! - Every file write (regular file or symlink) is recorded with its layer
|
||||||
|
//! and source.
|
||||||
|
//! - When a later layer overwrites a path written by an earlier layer, a
|
||||||
|
//! `[COLLISION-WARN]` line is emitted to stderr. The build-time
|
||||||
|
//! `scripts/validate-collision-log.sh` greps logs for that marker.
|
||||||
|
//! - Layer 3 (`ConfigPostInstall`) is the *intentional override* layer — its
|
||||||
|
//! whole purpose is to overwrite package defaults. Collisions from Layer 3
|
||||||
|
//! are suppressed by design and never warn.
|
||||||
|
//! - Known-safe override path pairs (e.g. `/etc/init.d/` over
|
||||||
|
//! `/usr/lib/init.d/`, mirroring the init system's `config_for_dirs()`
|
||||||
|
//! priority) are also suppressed.
|
||||||
|
//! - Strict mode (`REDBEAR_INSTALLER_STRICT_COLLISIONS=1`) promotes
|
||||||
|
//! collisions to hard errors via `anyhow::bail!`. Default is warn-only.
|
||||||
|
//!
|
||||||
|
//! ## What is NOT tracked
|
||||||
|
//!
|
||||||
|
//! Directory creation. `fs::create_dir_all` is idempotent; two layers
|
||||||
|
//! creating the same directory is a no-op, not a collision. Only regular
|
||||||
|
//! files and symlinks are recorded.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
|
/// Installer file-write layers, in write order. The numeric value matches
|
||||||
|
/// the layer numbering in AGENTS.md § "Installer File Layering" and is used
|
||||||
|
/// in user-facing warning text.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum Layer {
|
||||||
|
/// `[[files]]` entries with `postinstall = false`. Written first.
|
||||||
|
ConfigPreInstall = 1,
|
||||||
|
/// Package staging via `install_packages()`. Layer 2 overwriting
|
||||||
|
/// Layer 1 is the D-Bus regression class (silent loss of config).
|
||||||
|
PackageStaging = 2,
|
||||||
|
/// `[[files]]` entries with `postinstall = true`. Intentional override
|
||||||
|
/// layer — collisions from this layer never warn by design.
|
||||||
|
ConfigPostInstall = 3,
|
||||||
|
/// Files the installer itself writes at the end of `install_dir()`
|
||||||
|
/// (`/etc/passwd`, `/etc/shadow`, `/etc/group`).
|
||||||
|
InstallerBuiltin = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layer {
|
||||||
|
/// Numeric layer identifier as documented in AGENTS.md. Stable across
|
||||||
|
/// releases — used in `[COLLISION-WARN]` output.
|
||||||
|
pub fn as_u8(self) -> u8 {
|
||||||
|
self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where a tracked file came from.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ProvenanceSource {
|
||||||
|
/// `[[files]]` entry. The string is the config-visible path
|
||||||
|
/// (leading-slash form, e.g. `"/etc/passwd"`).
|
||||||
|
ConfigFile(String),
|
||||||
|
/// Package staging. The string is a package identifier, or `"staged"`
|
||||||
|
/// when the writing package cannot be identified (the common case —
|
||||||
|
/// `install_packages()` does not report per-file provenance).
|
||||||
|
Package(String),
|
||||||
|
/// Installer-builtin file (`/etc/passwd`, `/etc/shadow`, `/etc/group`).
|
||||||
|
InstallerBuiltin,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provenance record for one file path.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FileProvenance {
|
||||||
|
/// What wrote the file.
|
||||||
|
pub source: ProvenanceSource,
|
||||||
|
/// Which install layer wrote it.
|
||||||
|
pub layer: Layer,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One collision event recorded by the tracker.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Collision {
|
||||||
|
/// Absolute filesystem path that was overwritten.
|
||||||
|
pub path: PathBuf,
|
||||||
|
/// Provenance of the earlier (overwritten) write.
|
||||||
|
pub previous: FileProvenance,
|
||||||
|
/// Provenance of the later (overwriting) write.
|
||||||
|
pub current: FileProvenance,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marker emitted on stderr for every collision warning. The build-time
|
||||||
|
/// `scripts/validate-collision-log.sh` script greps logs for this token.
|
||||||
|
pub const WARN_MARKER: &str = "[COLLISION-WARN]";
|
||||||
|
|
||||||
|
/// Marker emitted on stderr when strict mode promotes a collision to an
|
||||||
|
/// error. Paired with `WARN_MARKER` in the same message so log-grepping
|
||||||
|
/// for either token catches every collision.
|
||||||
|
pub const ERROR_MARKER: &str = "[COLLISION-ERROR]";
|
||||||
|
|
||||||
|
/// Environment variable name that opts into strict mode (promotes
|
||||||
|
/// collisions to hard errors). Default is warn-only.
|
||||||
|
pub const STRICT_ENV_VAR: &str = "REDBEAR_INSTALLER_STRICT_COLLISIONS";
|
||||||
|
|
||||||
|
/// Pairs of `(override_dir, package_default_dir)`. When the current write
|
||||||
|
/// is under `override_dir` and the previous write was under
|
||||||
|
/// `package_default_dir`, the collision is considered intentional and is
|
||||||
|
/// suppressed. These mirror the init system's `config_for_dirs()` priority
|
||||||
|
/// directories documented in AGENTS.md § "Init Service File Ownership".
|
||||||
|
const KNOWN_SAFE_OVERRIDE_PAIRS: &[(&str, &str)] = &[
|
||||||
|
// Config overrides at /etc/init.d/ intentionally replace package
|
||||||
|
// defaults at /usr/lib/init.d/ for the same filename. The init
|
||||||
|
// system's config_for_dirs() BTreeMap gives /etc/init.d/ priority.
|
||||||
|
("/etc/init.d", "/usr/lib/init.d"),
|
||||||
|
// Likewise for environment.d — config overrides at /etc/ replace
|
||||||
|
// package defaults at /usr/lib/.
|
||||||
|
("/etc/environment.d", "/usr/lib/environment.d"),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Returns true if `(previous_config_path, current_config_path)` matches a
|
||||||
|
/// known-safe override pair: previous under a package default dir, current
|
||||||
|
/// under the matching config override dir. Both arguments are absolute
|
||||||
|
/// config-visible paths (leading `/`).
|
||||||
|
fn is_known_safe_override(previous_config_path: &str, current_config_path: &str) -> bool {
|
||||||
|
for &(override_dir, default_dir) in KNOWN_SAFE_OVERRIDE_PAIRS {
|
||||||
|
let curr_is_override =
|
||||||
|
current_config_path == override_dir || is_dir_member(current_config_path, override_dir);
|
||||||
|
let prev_is_default =
|
||||||
|
previous_config_path == default_dir || is_dir_member(previous_config_path, default_dir);
|
||||||
|
if curr_is_override && prev_is_default {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if `child` is a file path strictly inside `parent` (not `parent`
|
||||||
|
/// itself). Both arguments are absolute config-visible paths with no
|
||||||
|
/// trailing slash. Uses byte comparison to stay I/O-free.
|
||||||
|
fn is_dir_member(child: &str, parent: &str) -> bool {
|
||||||
|
// We expect paths like "/etc/init.d/foo" inside "/etc/init.d".
|
||||||
|
let parent_with_slash = format!("{parent}/");
|
||||||
|
child.starts_with(&parent_with_slash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// File-write provenance tracker for `install_dir()`.
|
||||||
|
///
|
||||||
|
/// Construct with [`CollisionTracker::new`] to pick up strict mode from the
|
||||||
|
/// environment, or with [`CollisionTracker::new_with_strict`] for tests and
|
||||||
|
/// explicit control. Call [`CollisionTracker::record`] before each file
|
||||||
|
/// write, and [`CollisionTracker::scan_package_staging`] once after
|
||||||
|
/// `install_packages()` returns.
|
||||||
|
pub struct CollisionTracker {
|
||||||
|
/// Map from absolute filesystem path to the most recent provenance.
|
||||||
|
files: HashMap<PathBuf, FileProvenance>,
|
||||||
|
/// Map from absolute filesystem path back to the leading-`/` config
|
||||||
|
/// path, when known. Lets the known-safe override check compare
|
||||||
|
/// previous vs. current config-visible paths even when both resolves
|
||||||
|
/// land at the same physical file (e.g. via a symlinked /etc/init.d).
|
||||||
|
config_paths: HashMap<PathBuf, String>,
|
||||||
|
strict: bool,
|
||||||
|
collisions: Vec<Collision>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CollisionTracker {
|
||||||
|
/// Construct a tracker, reading strict mode from the
|
||||||
|
/// `REDBEAR_INSTALLER_STRICT_COLLISIONS` environment variable.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let strict = std::env::var(STRICT_ENV_VAR)
|
||||||
|
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
Self::new_with_strict(strict)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a tracker with explicit strict-mode control.
|
||||||
|
///
|
||||||
|
/// Tests should prefer this over [`Self::new`] to avoid env-var races
|
||||||
|
/// under parallel test execution.
|
||||||
|
pub fn new_with_strict(strict: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
files: HashMap::new(),
|
||||||
|
config_paths: HashMap::new(),
|
||||||
|
strict,
|
||||||
|
collisions: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of distinct file paths currently tracked.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.files.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the tracker has observed any file writes.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.files.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of collisions recorded (warnings emitted, or errors raised in
|
||||||
|
/// strict mode).
|
||||||
|
pub fn collision_count(&self) -> usize {
|
||||||
|
self.collisions.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Snapshot of recorded collisions (for test inspection and reporting).
|
||||||
|
pub fn collisions(&self) -> &[Collision] {
|
||||||
|
&self.collisions
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether strict mode is enabled on this tracker.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_strict(&self) -> bool {
|
||||||
|
self.strict
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Record a file write at `abs_path` (absolute filesystem path) with
|
||||||
|
/// the given provenance.
|
||||||
|
///
|
||||||
|
/// `config_path` is the leading-`/` config-visible path when known
|
||||||
|
/// (used for the known-safe override check), or `None` for paths whose
|
||||||
|
/// config-visible form is not meaningful.
|
||||||
|
///
|
||||||
|
/// # Collisions
|
||||||
|
///
|
||||||
|
/// A collision is when the same `abs_path` was previously written by a
|
||||||
|
/// *different* layer. Collisions emit `[COLLISION-WARN]` on stderr and
|
||||||
|
/// are recorded. In strict mode they additionally return `Err`.
|
||||||
|
///
|
||||||
|
/// # Suppression
|
||||||
|
///
|
||||||
|
/// Warnings are suppressed when either:
|
||||||
|
/// - the current write is from `Layer::ConfigPostInstall` (the
|
||||||
|
/// intentional override layer), or
|
||||||
|
/// - the previous config path and the current config path form a
|
||||||
|
/// known-safe override pair (e.g. `/etc/init.d/X` over
|
||||||
|
/// `/usr/lib/init.d/X`).
|
||||||
|
///
|
||||||
|
/// Suppressed collisions are still recorded in the `files` map (the
|
||||||
|
/// latest provenance wins) but do not emit a warning and do not
|
||||||
|
/// increment `collision_count()`.
|
||||||
|
pub fn record(
|
||||||
|
&mut self,
|
||||||
|
abs_path: PathBuf,
|
||||||
|
provenance: FileProvenance,
|
||||||
|
config_path: Option<&str>,
|
||||||
|
) -> Result<()> {
|
||||||
|
// Capture previous state BEFORE any mutation. The known-safe
|
||||||
|
// override check compares the previous config_path against the
|
||||||
|
// current one; if we let the insert below clobber the map first,
|
||||||
|
// we'd compare the new path against itself.
|
||||||
|
let previous_provenance = self.files.get(&abs_path).cloned();
|
||||||
|
let previous_config_path = self.config_paths.get(&abs_path).cloned();
|
||||||
|
|
||||||
|
if let Some(cp) = config_path {
|
||||||
|
self.config_paths.insert(abs_path.clone(), cp.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(previous) = previous_provenance else {
|
||||||
|
self.files.insert(abs_path, provenance);
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Same-layer re-write is not a collision. Config merges already
|
||||||
|
// deduplicate same-path entries; package staging may legitimately
|
||||||
|
// touch a file twice (e.g. an inner archive extraction).
|
||||||
|
if previous.layer == provenance.layer {
|
||||||
|
self.files.insert(abs_path, provenance);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer 3 is the intentional override layer by design. Its whole
|
||||||
|
// purpose is to overwrite package defaults — collisions from it
|
||||||
|
// must not warn or the override mechanism would be unusable.
|
||||||
|
let is_layer3_override = provenance.layer == Layer::ConfigPostInstall;
|
||||||
|
|
||||||
|
// Known-safe override path pairs (e.g. /etc/init.d over
|
||||||
|
// /usr/lib/init.d). Only meaningful when both layers recorded a
|
||||||
|
// config-visible path for the same abs_path.
|
||||||
|
let is_known_safe = match (previous_config_path.as_deref(), config_path) {
|
||||||
|
(Some(prev_cp), Some(curr_cp)) => is_known_safe_override(prev_cp, curr_cp),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_layer3_override || is_known_safe {
|
||||||
|
self.files.insert(abs_path, provenance);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real collision — warn (and bail in strict mode).
|
||||||
|
let collision = Collision {
|
||||||
|
path: abs_path.clone(),
|
||||||
|
previous: previous.clone(),
|
||||||
|
current: provenance.clone(),
|
||||||
|
};
|
||||||
|
self.collisions.push(collision.clone());
|
||||||
|
emit_warning(&collision);
|
||||||
|
self.files.insert(abs_path, provenance);
|
||||||
|
|
||||||
|
if self.strict {
|
||||||
|
bail!(
|
||||||
|
"{ERROR_MARKER} strict mode ({STRICT_ENV_VAR}=1): collision at {} \
|
||||||
|
— {} layer {} overwritten by {} layer {}. Aborting install. \
|
||||||
|
Fix the conflicting paths, or unset {STRICT_ENV_VAR} to demote \
|
||||||
|
this to a warning.",
|
||||||
|
collision.path.display(),
|
||||||
|
source_label(&collision.previous.source),
|
||||||
|
collision.previous.layer.as_u8(),
|
||||||
|
source_label(&collision.current.source),
|
||||||
|
collision.current.layer.as_u8(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walk `dest` after `install_packages()` returns, recording every file
|
||||||
|
/// the package staging wrote.
|
||||||
|
///
|
||||||
|
/// Files already tracked from Layer 1 trigger the Layer 2 overwrites
|
||||||
|
/// Layer 1 collision (the D-Bus regression class). Files not previously
|
||||||
|
/// tracked are recorded with `Layer::PackageStaging` provenance.
|
||||||
|
///
|
||||||
|
/// Directories are not tracked — `fs::create_dir_all` is idempotent and
|
||||||
|
/// directory paths are shared infrastructure. Only regular files and
|
||||||
|
/// symlinks are recorded.
|
||||||
|
///
|
||||||
|
/// I/O errors during the walk are tolerated (treated as "no file here")
|
||||||
|
/// so a partially-populated tree does not abort the install. The
|
||||||
|
/// collision decision is made from what we *can* read.
|
||||||
|
pub fn scan_package_staging(&mut self, dest: &Path) -> Result<()> {
|
||||||
|
if !dest.exists() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
walk_for_files(dest, dest, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CollisionTracker {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursive walker that records every file (regular or symlink) under
|
||||||
|
/// `root` as a Layer 2 (PackageStaging) write.
|
||||||
|
fn walk_for_files(root: &Path, current: &Path, tracker: &mut CollisionTracker) -> Result<()> {
|
||||||
|
let read_dir = match std::fs::read_dir(current) {
|
||||||
|
Ok(rd) => rd,
|
||||||
|
// Tolerate unreadable subdirs — they contribute no files.
|
||||||
|
Err(_) => return Ok(()),
|
||||||
|
};
|
||||||
|
for entry in read_dir {
|
||||||
|
let Ok(entry) = entry else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(file_type) = entry.file_type() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let abs_path = entry.path();
|
||||||
|
if file_type.is_symlink() || file_type.is_file() {
|
||||||
|
let provenance = FileProvenance {
|
||||||
|
source: ProvenanceSource::Package("staged".to_string()),
|
||||||
|
layer: Layer::PackageStaging,
|
||||||
|
};
|
||||||
|
// For package-staged files the config-visible path is the path
|
||||||
|
// under `root` (with a leading `/`). This matches how a config
|
||||||
|
// `[[files]]` entry would address the same file.
|
||||||
|
let config_visible = match abs_path.strip_prefix(root) {
|
||||||
|
Ok(rel) => format!("/{}", rel.display()),
|
||||||
|
Err(_) => abs_path.to_string_lossy().into_owned(),
|
||||||
|
};
|
||||||
|
tracker.record(abs_path, provenance, Some(&config_visible))?;
|
||||||
|
} else if file_type.is_dir() {
|
||||||
|
walk_for_files(root, &abs_path, tracker)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit a single `[COLLISION-WARN]` line on stderr. The build's
|
||||||
|
/// `validate-collision-log.sh` greps for this token after the install
|
||||||
|
/// finishes.
|
||||||
|
fn emit_warning(c: &Collision) {
|
||||||
|
eprintln!(
|
||||||
|
"{WARN_MARKER} {} layer {} overwritten by layer {} ({} -> {}). \
|
||||||
|
Set {STRICT_ENV_VAR}=1 to promote this to an error.",
|
||||||
|
c.path.display(),
|
||||||
|
c.previous.layer.as_u8(),
|
||||||
|
c.current.layer.as_u8(),
|
||||||
|
source_label(&c.previous.source),
|
||||||
|
source_label(&c.current.source),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Short human-readable label for a provenance source, used in warning and
|
||||||
|
/// error text.
|
||||||
|
fn source_label(s: &ProvenanceSource) -> &'static str {
|
||||||
|
match s {
|
||||||
|
ProvenanceSource::ConfigFile(_) => "config",
|
||||||
|
ProvenanceSource::Package(_) => "package",
|
||||||
|
ProvenanceSource::InstallerBuiltin => "installer",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
fn is_false(value: &bool) -> bool {
|
||||||
|
!*value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct FileConfig {
|
||||||
|
pub path: String,
|
||||||
|
pub data: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub symlink: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub directory: bool,
|
||||||
|
pub mode: Option<u32>,
|
||||||
|
pub uid: Option<u32>,
|
||||||
|
pub gid: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub recursive_chown: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub postinstall: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "is_false")]
|
||||||
|
pub append: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileConfig {
|
||||||
|
pub fn new_file(path: String, data: String) -> FileConfig {
|
||||||
|
FileConfig {
|
||||||
|
path,
|
||||||
|
data,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_directory(path: String) -> FileConfig {
|
||||||
|
FileConfig {
|
||||||
|
path,
|
||||||
|
data: String::new(),
|
||||||
|
directory: true,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_mod(&mut self, mode: u32, uid: u32, gid: u32) -> &mut FileConfig {
|
||||||
|
self.mode = Some(mode);
|
||||||
|
self.uid = Some(uid);
|
||||||
|
self.gid = Some(gid);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_recursive_mod(&mut self, mode: u32, uid: u32, gid: u32) -> &mut FileConfig {
|
||||||
|
self.with_mod(mode, uid, gid);
|
||||||
|
self.recursive_chown = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FileConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.path)?;
|
||||||
|
if self.symlink {
|
||||||
|
write!(f, " -> {}", self.data)?;
|
||||||
|
} else if self.directory {
|
||||||
|
write!(f, " type=dir")?;
|
||||||
|
if self.recursive_chown {
|
||||||
|
write!(f, " chown=yes")?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write!(f, " size={}", crate::format_bytes(self.data.len() as u64))?;
|
||||||
|
if self.postinstall {
|
||||||
|
write!(f, "!")?;
|
||||||
|
}
|
||||||
|
if self.append {
|
||||||
|
write!(f, " append=yes")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(uid) = self.uid {
|
||||||
|
write!(f, " uid={}", uid)?;
|
||||||
|
}
|
||||||
|
if let Some(uid) = self.uid {
|
||||||
|
write!(f, " gid={}", uid)?;
|
||||||
|
}
|
||||||
|
if let Some(mode) = self.mode {
|
||||||
|
write!(f, " mode={:3o}", mode)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
use anyhow::{Context, Result};
|
||||||
|
use libc::{gid_t, uid_t};
|
||||||
|
|
||||||
|
use std::ffi::{CString, OsStr};
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::{Error, Write};
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use std::os::unix::fs::{symlink, PermissionsExt};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn chown<P: AsRef<Path>>(path: P, uid: uid_t, gid: gid_t, recursive: bool) -> Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let c_path = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||||
|
if unsafe { libc::chown(c_path.as_ptr(), uid, gid) } != 0 {
|
||||||
|
return Err(Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if recursive && path.is_dir() {
|
||||||
|
for entry_res in fs::read_dir(path)? {
|
||||||
|
let entry = entry_res?;
|
||||||
|
chown(entry.path(), uid, gid, recursive)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Rewrite impls
|
||||||
|
impl crate::FileConfig {
|
||||||
|
pub(crate) fn create<P: AsRef<Path>>(&self, prefix: P) -> Result<()> {
|
||||||
|
let path = self.path.trim_start_matches('/');
|
||||||
|
let target_file = prefix.as_ref().join(path);
|
||||||
|
|
||||||
|
if self.directory {
|
||||||
|
println!("Create directory {}", target_file.display());
|
||||||
|
fs::create_dir_all(&target_file)
|
||||||
|
.with_context(|| format!("failed to create directory {}", target_file.display()))?;
|
||||||
|
self.apply_perms(&target_file)?;
|
||||||
|
return Ok(());
|
||||||
|
} else if let Some(parent) = target_file.parent() {
|
||||||
|
println!("Create file parent {}", parent.display());
|
||||||
|
fs::create_dir_all(parent)
|
||||||
|
.with_context(|| format!("failed to create file parent {}", parent.display()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.symlink {
|
||||||
|
println!("Create symlink {} to {}", target_file.display(), self.data);
|
||||||
|
if target_file.is_symlink() {
|
||||||
|
fs::remove_file(&target_file).with_context(|| {
|
||||||
|
format!("failed to remove old symlink {}", target_file.display())
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
symlink(&OsStr::new(&self.data), &target_file).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"failed to create symlink {} to {}",
|
||||||
|
target_file.display(),
|
||||||
|
self.data
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let action = if self.append { "Append" } else { "Create" };
|
||||||
|
println!("{action} file {}", target_file.display());
|
||||||
|
|
||||||
|
let mut file = if self.append {
|
||||||
|
fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&target_file)
|
||||||
|
.with_context(|| format!("failed to append file {}", target_file.display()))?
|
||||||
|
} else {
|
||||||
|
File::create(&target_file)
|
||||||
|
.with_context(|| format!("failed to create file {}", target_file.display()))?
|
||||||
|
};
|
||||||
|
file.write_all(self.data.as_bytes())?;
|
||||||
|
|
||||||
|
self.apply_perms(target_file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_perms<P: AsRef<Path>>(&self, target: P) -> Result<()> {
|
||||||
|
let path = target.as_ref();
|
||||||
|
let mode = self
|
||||||
|
.mode
|
||||||
|
.unwrap_or_else(|| if self.directory { 0o0755 } else { 0o0644 });
|
||||||
|
let uid = self.uid.unwrap_or(!0);
|
||||||
|
let gid = self.gid.unwrap_or(!0);
|
||||||
|
|
||||||
|
// chmod
|
||||||
|
fs::set_permissions(path, fs::Permissions::from_mode(mode))
|
||||||
|
.with_context(|| format!("failed to set permissions on {}", path.display()))?;
|
||||||
|
|
||||||
|
// chown
|
||||||
|
chown(path, uid, gid, self.recursive_chown)
|
||||||
|
.with_context(|| format!("failed to chown {}", path.display()))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct GeneralConfig {
|
||||||
|
/// Specify a path where cookbook exists, all packages will be installed locally
|
||||||
|
pub cookbook: Option<String>,
|
||||||
|
/// Allow prompts for missing information such as user password
|
||||||
|
pub prompt: Option<bool>,
|
||||||
|
/// Total filesystem size in MB
|
||||||
|
pub filesystem_size: Option<u32>,
|
||||||
|
/// EFI partition size in MB, default to 2MB
|
||||||
|
pub efi_partition_size: Option<u32>,
|
||||||
|
/// Skip disk partitioning, assume whole disk is a partition
|
||||||
|
pub skip_partitions: Option<bool>,
|
||||||
|
/// Set a plain text password to encrypt the disk
|
||||||
|
pub encrypt_disk: Option<String>,
|
||||||
|
/// Use live disk for bootloader config, default is false
|
||||||
|
pub live_disk: Option<bool>,
|
||||||
|
/// If set, write bootloader disk into this path
|
||||||
|
pub write_bootloader: Option<String>,
|
||||||
|
/// Use AR to write files instead of FUSE-based mount
|
||||||
|
/// (bypasses FUSE, but slower and requires namespaced context such as "podman unshare")
|
||||||
|
pub no_mount: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GeneralConfig {
|
||||||
|
/// Merge two config, "other" is more dominant
|
||||||
|
pub(super) fn merge(&mut self, other: GeneralConfig) {
|
||||||
|
if let Some(cookbook) = other.cookbook {
|
||||||
|
self.cookbook = Some(cookbook);
|
||||||
|
}
|
||||||
|
self.filesystem_size = other.filesystem_size.or(self.filesystem_size);
|
||||||
|
self.efi_partition_size = other.efi_partition_size.or(self.efi_partition_size);
|
||||||
|
self.skip_partitions = other.skip_partitions.or(self.skip_partitions);
|
||||||
|
if let Some(encrypt_disk) = other.encrypt_disk {
|
||||||
|
self.encrypt_disk = Some(encrypt_disk);
|
||||||
|
}
|
||||||
|
self.live_disk = other.live_disk.or(self.live_disk);
|
||||||
|
if let Some(write_bootloader) = other.write_bootloader {
|
||||||
|
self.write_bootloader = Some(write_bootloader);
|
||||||
|
}
|
||||||
|
self.no_mount = other.no_mount.or(self.no_mount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::fs;
|
||||||
|
use std::mem;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
use anyhow::Context;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::PackageConfig;
|
||||||
|
|
||||||
|
pub mod file;
|
||||||
|
#[cfg(feature = "installer")]
|
||||||
|
pub mod file_impl;
|
||||||
|
pub mod general;
|
||||||
|
pub mod package;
|
||||||
|
pub mod user;
|
||||||
|
|
||||||
|
/// A named package group. A group's `packages` list may contain either a
|
||||||
|
/// direct package name or the name of another group; the latter is expanded
|
||||||
|
/// recursively. Cycles are detected and reported as configuration errors.
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct PackageGroup {
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: String,
|
||||||
|
/// Member names: either packages (keys of `Config.packages`) or other
|
||||||
|
/// group names (keys of `Config.package_groups`). Resolved at config load.
|
||||||
|
#[serde(default)]
|
||||||
|
pub packages: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct Config {
|
||||||
|
#[serde(default)]
|
||||||
|
pub include: Vec<PathBuf>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub general: general::GeneralConfig,
|
||||||
|
#[serde(default)]
|
||||||
|
pub packages: BTreeMap<String, package::PackageConfig>,
|
||||||
|
/// Named package groups. Members may reference other group names; the
|
||||||
|
/// resolution is recursive with cycle detection. Groups are resolved
|
||||||
|
/// during `from_file()` and contribute to `packages` so downstream
|
||||||
|
/// consumers see a flat package map.
|
||||||
|
#[serde(default)]
|
||||||
|
pub package_groups: BTreeMap<String, PackageGroup>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub files: Vec<file::FileConfig>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub users: BTreeMap<String, user::UserConfig>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub groups: BTreeMap<String, user::GroupConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// Load installer config from a TOML path. Resolves all
|
||||||
|
/// `package_groups` references into the flat `packages` map. Group
|
||||||
|
/// resolution is recursive with cycle detection.
|
||||||
|
pub fn from_file(path: &Path) -> Result<Self> {
|
||||||
|
let mut config: Config = match fs::read_to_string(&path) {
|
||||||
|
Ok(config_data) => match toml::from_str(&config_data) {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to decode '{}': {}", path.display(), err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
bail!("failed to read '{}': {}", path.display(), err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let config_dir = path.parent().unwrap();
|
||||||
|
|
||||||
|
let mut configs = mem::take(&mut config.include)
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| {
|
||||||
|
Config::from_file(&config_dir.join(&path))
|
||||||
|
.with_context(|| format!("Importing from {}", path.display()))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<Config>>>()?;
|
||||||
|
configs.push(config); // Put ourself last to ensure that it overwrites anything else.
|
||||||
|
|
||||||
|
config = configs.remove(0);
|
||||||
|
|
||||||
|
for other_config in configs {
|
||||||
|
config.merge(other_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.resolve_package_groups()?;
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve every `package_groups.*` entry, expanding each group's
|
||||||
|
/// members recursively. Each resolved member that is itself a group
|
||||||
|
/// name contributes its own members. Cycles are detected and reported.
|
||||||
|
/// Resolved members that are package names are added to `packages`
|
||||||
|
/// (respecting the `Build("ignore")` semantics inherited from group
|
||||||
|
/// members — if any group member has `ignore`, the resolved package
|
||||||
|
/// keeps `ignore`).
|
||||||
|
pub fn resolve_package_groups(&mut self) -> Result<()> {
|
||||||
|
let mut resolved: BTreeMap<String, PackageConfig> = BTreeMap::new();
|
||||||
|
// Collect group keys in stable order so resolution is deterministic
|
||||||
|
// (BTreeMap iterates alphabetically).
|
||||||
|
let group_keys: Vec<String> = self.package_groups.keys().cloned().collect();
|
||||||
|
for name in &group_keys {
|
||||||
|
// Expand each group at most once: collect members transitively,
|
||||||
|
// detect cycles via a stack of currently-visiting names.
|
||||||
|
let mut resolved_here: BTreeSet<String> = BTreeSet::new();
|
||||||
|
let mut visiting: Vec<String> = Vec::new();
|
||||||
|
let mut expanded_groups: BTreeSet<String> = BTreeSet::new();
|
||||||
|
self.expand_group(name, &mut visiting, &mut resolved_here, &mut expanded_groups)?;
|
||||||
|
for pkg in &resolved_here {
|
||||||
|
// Mark a group-expanded package for install using the SAME
|
||||||
|
// representation an explicit `pkg = {}` entry deserializes to
|
||||||
|
// (Spec with all-None fields). PackageConfig::default() is the
|
||||||
|
// `Empty` unit variant, which is untagged and cannot be
|
||||||
|
// serialized to TOML — `toml::to_string_pretty(&config)` (used
|
||||||
|
// to embed filesystem.toml) then panics with UnsupportedType.
|
||||||
|
// An explicit `[packages]` entry still overrides this via the
|
||||||
|
// `or_insert` + later overlay below.
|
||||||
|
resolved.entry(pkg.clone()).or_insert(PackageConfig::Spec {
|
||||||
|
version: None,
|
||||||
|
git: None,
|
||||||
|
path: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Merge resolved group packages into the flat `packages` map.
|
||||||
|
// Existing `[packages]` entries win (last-write-wins merge: the
|
||||||
|
// group-resolved entry is inserted first via `entry().or_insert`,
|
||||||
|
// and only then the explicit map is overlaid on top, so explicit
|
||||||
|
// overrides — including `Build("ignore")` — take precedence).
|
||||||
|
for (name, cfg) in resolved {
|
||||||
|
self.packages.entry(name).or_insert(cfg);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_group(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
visiting: &mut Vec<String>,
|
||||||
|
// ACTUAL package names accumulated so far (this is what gets installed).
|
||||||
|
resolved: &mut BTreeSet<String>,
|
||||||
|
// Group names already fully expanded — tracked SEPARATELY from
|
||||||
|
// `resolved` so a group's own name never leaks into the package set
|
||||||
|
// (a nested-group reference like `kde-desktop` -> `dbus-services` must
|
||||||
|
// expand to expat/dbus, NOT add a bogus "dbus-services" package that
|
||||||
|
// `repo` then fails to cook with "Package not found").
|
||||||
|
expanded: &mut BTreeSet<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
if expanded.contains(name) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if visiting.iter().any(|n| n == name) {
|
||||||
|
let mut cycle = visiting.clone();
|
||||||
|
cycle.push(name.to_string());
|
||||||
|
bail!(
|
||||||
|
"package_groups cycle: {}",
|
||||||
|
cycle.join(" -> ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let group = match self.package_groups.get(name) {
|
||||||
|
Some(g) => g,
|
||||||
|
None => {
|
||||||
|
// Member that names a missing group is treated as an error
|
||||||
|
// (silently dropping it has historically hidden typos like
|
||||||
|
// `kde-desktop = { packages = ["kf6-kio", "kf6-kwd"] }`
|
||||||
|
// where one entry is a typo for a group).
|
||||||
|
let mut known: Vec<String> = self.package_groups.keys().cloned().collect();
|
||||||
|
known.sort();
|
||||||
|
bail!(
|
||||||
|
"package_groups member '{}' is neither a package nor a known group; \
|
||||||
|
known groups: [{}]",
|
||||||
|
name,
|
||||||
|
known.join(", ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
visiting.push(name.to_string());
|
||||||
|
for member in &group.packages {
|
||||||
|
if self.package_groups.contains_key(member) {
|
||||||
|
self.expand_group(member, visiting, resolved, expanded)?;
|
||||||
|
} else {
|
||||||
|
// A leaf member: an actual package name. (If it names neither a
|
||||||
|
// group nor a real package, the missing-package error surfaces
|
||||||
|
// later at repo/install time.)
|
||||||
|
resolved.insert(member.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visiting.pop();
|
||||||
|
// Mark this GROUP as expanded (dedup / short-circuit only). Do NOT put
|
||||||
|
// it in `resolved` — it is a group, not a package.
|
||||||
|
expanded.insert(name.to_string());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load hardcoded install config to fetch bootloaders
|
||||||
|
pub fn bootloader_config() -> Self {
|
||||||
|
let mut bootloader_config = Config::default();
|
||||||
|
// TODO: This is unused
|
||||||
|
bootloader_config.files.push(file::FileConfig {
|
||||||
|
path: "/etc/pkg.d/50_redox".to_string(),
|
||||||
|
data: "https://static.redox-os.org/pkg".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
bootloader_config
|
||||||
|
.packages
|
||||||
|
.insert("bootloader".to_string(), PackageConfig::default());
|
||||||
|
bootloader_config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge(&mut self, other: Config) {
|
||||||
|
assert!(self.include.is_empty());
|
||||||
|
assert!(other.include.is_empty());
|
||||||
|
|
||||||
|
let Config {
|
||||||
|
include: _,
|
||||||
|
general: other_general,
|
||||||
|
packages: other_packages,
|
||||||
|
package_groups: other_groups,
|
||||||
|
files: other_files,
|
||||||
|
users: other_users,
|
||||||
|
groups: other_groups_users,
|
||||||
|
} = other;
|
||||||
|
|
||||||
|
self.general.merge(other_general);
|
||||||
|
|
||||||
|
for (package, package_config) in other_packages {
|
||||||
|
self.packages.insert(package, package_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package groups from later configs override earlier ones (last writer
|
||||||
|
// wins); group resolution happens once after the full merge via
|
||||||
|
// `resolve_package_groups()`.
|
||||||
|
for (group_name, group_cfg) in other_groups {
|
||||||
|
self.package_groups.insert(group_name, group_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// File entries from later-included configs override earlier ones for
|
||||||
|
// the same path. Previously this was `self.files.extend(other_files)`,
|
||||||
|
// which silently accumulated duplicate paths and left the winner to
|
||||||
|
// whichever entry the installer wrote last — an invisible, order-
|
||||||
|
// dependent override that is a frequent source of "my config edit did
|
||||||
|
// nothing" confusion (e.g. an init service redefined by a legacy
|
||||||
|
// overlay config). Make the override explicit and visible: warn on the
|
||||||
|
// console and replace in place so the last definition deterministically
|
||||||
|
// wins with no duplicate left in the list.
|
||||||
|
for file in other_files {
|
||||||
|
if let Some(existing) = self.files.iter_mut().find(|f| f.path == file.path) {
|
||||||
|
eprintln!(
|
||||||
|
"config: WARNING: file '{}' redefined by a later-included config \
|
||||||
|
(override — last definition wins)",
|
||||||
|
file.path
|
||||||
|
);
|
||||||
|
*existing = file;
|
||||||
|
} else {
|
||||||
|
self.files.push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (user, user_config) in other_users {
|
||||||
|
self.users.insert(user, user_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (group, group_config) in other_groups_users {
|
||||||
|
self.groups.insert(group, group_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Config {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, "files:")?;
|
||||||
|
for file in &self.files {
|
||||||
|
writeln!(f, "- {}", file)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "users:")?;
|
||||||
|
for (name, user) in &self.users {
|
||||||
|
writeln!(f, "- {}:{}", name, user)?;
|
||||||
|
}
|
||||||
|
write!(f, "packages: ")?;
|
||||||
|
for name in self.packages.keys() {
|
||||||
|
write!(f, " {}", name)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PackageConfig {
|
||||||
|
Empty,
|
||||||
|
Build(String),
|
||||||
|
|
||||||
|
// TODO: Sum type
|
||||||
|
Spec {
|
||||||
|
version: Option<String>,
|
||||||
|
git: Option<String>,
|
||||||
|
path: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PackageConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct UserConfig {
|
||||||
|
pub password: Option<String>,
|
||||||
|
pub uid: Option<u32>,
|
||||||
|
pub gid: Option<u32>,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub home: Option<String>,
|
||||||
|
pub shell: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct GroupConfig {
|
||||||
|
pub gid: Option<u32>,
|
||||||
|
// FIXME move this to the UserConfig struct as extra_groups
|
||||||
|
pub members: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for UserConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(uid) = &self.uid {
|
||||||
|
write!(f, " uid={}", uid)?;
|
||||||
|
}
|
||||||
|
if let Some(gid) = &self.gid {
|
||||||
|
write!(f, " gid={}", gid)?;
|
||||||
|
}
|
||||||
|
if let Some(name) = &self.name {
|
||||||
|
write!(f, " name={}", name)?;
|
||||||
|
}
|
||||||
|
if let Some(home) = &self.home {
|
||||||
|
write!(f, " home={}", home)?;
|
||||||
|
}
|
||||||
|
if let Some(shell) = &self.shell {
|
||||||
|
write!(f, " shell={}", shell)?;
|
||||||
|
}
|
||||||
|
if self.password.as_ref().is_some_and(|s| !s.is_empty()) {
|
||||||
|
write!(f, " password=yes")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "crt0"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "crt0"
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
//! crt0
|
|
||||||
|
|
||||||
#![no_std]
|
|
||||||
#![feature(linkage)]
|
|
||||||
|
|
||||||
use core::{
|
|
||||||
arch::global_asm,
|
|
||||||
ffi::{c_char, c_int},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
mov x0, sp
|
|
||||||
and sp, x0, #0xfffffffffffffff0 //align sp
|
|
||||||
bl relibc_crt0
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
.type _start, @function
|
|
||||||
_start:
|
|
||||||
sub esp, 8
|
|
||||||
|
|
||||||
mov DWORD PTR [esp], 0x00001F80
|
|
||||||
# ldmxcsr [esp]
|
|
||||||
mov WORD PTR [esp], 0x037F
|
|
||||||
fldcw [esp]
|
|
||||||
|
|
||||||
add esp, 8
|
|
||||||
|
|
||||||
push esp
|
|
||||||
call relibc_crt0
|
|
||||||
.size _start, . - _start
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
.type _start, @function
|
|
||||||
_start:
|
|
||||||
mov rdi, rsp
|
|
||||||
and rsp, 0xFFFFFFFFFFFFFFF0
|
|
||||||
|
|
||||||
sub rsp, 8
|
|
||||||
|
|
||||||
mov DWORD PTR [rsp], 0x00001F80
|
|
||||||
ldmxcsr [rsp]
|
|
||||||
mov WORD PTR [rsp], 0x037F
|
|
||||||
fldcw [rsp]
|
|
||||||
|
|
||||||
add rsp, 8
|
|
||||||
|
|
||||||
call relibc_crt0
|
|
||||||
.size _start, . - _start
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
mv a0, sp
|
|
||||||
la t0, relibc_crt0
|
|
||||||
jalr ra, t0
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn relibc_crt0(sp: usize) -> ! {
|
|
||||||
// This wrapper ensures a dynamic libc.so can access a hidden main function
|
|
||||||
//TODO: common definition of types
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn main(argc: isize, argv: *mut *mut c_char, envp: *mut *mut c_char) -> c_int;
|
|
||||||
fn relibc_start_v1(
|
|
||||||
sp: usize,
|
|
||||||
main: unsafe extern "C" fn(
|
|
||||||
argc: isize,
|
|
||||||
argv: *mut *mut c_char,
|
|
||||||
envp: *mut *mut c_char,
|
|
||||||
) -> c_int,
|
|
||||||
) -> !;
|
|
||||||
}
|
|
||||||
unsafe { relibc_start_v1(sp, main) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[linkage = "weak"]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn relibc_panic(_pi: &::core::panic::PanicInfo) -> ! {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
#[linkage = "weak"]
|
|
||||||
pub unsafe fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
|
|
||||||
relibc_panic(pi)
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user