Compare commits

..

No commits in common. "master" and "nesemu1-1.2" have entirely different histories.

14484 changed files with 459400 additions and 1143218 deletions

View file

@ -6,8 +6,6 @@ AlignConsecutiveMacros: true
AlignConsecutiveDeclarations: false AlignConsecutiveDeclarations: false
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
AllowShortFunctionsOnASingleLine: false AllowShortFunctionsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
KeepEmptyLinesAtTheStartOfBlocks: true KeepEmptyLinesAtTheStartOfBlocks: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerAllOnOneLineOrOnePerLine: true
IncludeBlocks: Merge IncludeBlocks: Merge

View file

@ -1,31 +0,0 @@
# Run this command to always ignore formatting commits in git blame
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# Upgraded clang-format to 19.0.0git
89fc95fefd40413706ff1bdaac2e8d98b328dbf1
# vim c++ filetype in modelines
04c6bc478e082263d67c41bedbd033dde2d429eb
# Ran clang-format
f032b5570b4cd87c6bb4abb54c0b98e69c939955
# Applied clang-format update to repo
6e6fc38935054db0534d5af4fb99c6193305b946
# revert retabbing
2b315626f3af765cdfbc61114647412cdb798b3a
# more modeline errata
3a8e01a77a7c97af0b16fb1651b230cee7f7d4c6
# fix more vi modelines
2fc507c98f53a76718f61f9a36602f86b5ac0cc9
# flip et/noet in modelines
e16a7d8f3b8f906c3ef76e79f57f3adfc7f25186
# fix vi modelines
394d998315f613a888cc6b6c051d4163bdf5cd6f
# clang-format
c0eacf2eb1e1c0b3bd4f71f12fef258f5b249c3f
# ape-m1 formatting cleanup
da8baf2aa5ce93b958aca90a0ae69f537806324b
# Run clang-format on most sources
369f9740de4534c28d0e81ab2afc99decbb9a3e6
# Get rid of .internal.h convention in LIBC_INTRIN
86d884cce24d773e298a2714c1e3d91ecab9be45
# Remove .internal from more header filenames
31194165d2afca36c2315a6e7ca2f0797dde09e3

9
.gitattributes vendored
View file

@ -1,10 +1,5 @@
# -*- conf -*- # -*- conf -*-
*.gz binary *.gz binary
*.so binary /build/bootstrap/*.com binary
*.dll binary /build/bootstrap/*.com binary
*.dylib binary
/build/bootstrap/* binary
/usr/share/terminfo/* binary
/usr/share/terminfo/*/* binary
/usr/share/zoneinfo/* binary /usr/share/zoneinfo/* binary
/usr/share/zoneinfo/*/* binary

View file

@ -1,59 +0,0 @@
name: Low Severity Bugs
description: Used to report low severity bugs in cosmopolitan (e.g. cosmetic issues, non critical UI glitches)
title: "Bug: "
labels: ["bug", "low severity"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Please include information about your system, the steps to reproduce the bug,
and the version of cosmopolitan that you are using.
If possible, please provide a minimal code example that reproduces the bug.
You may also consider using function call tracing `--ftrace` or the lighter system call tracing `--strace`
for additional technical logging that may allow us to narrow down where the fault occurred.
- type: input
id: contact
attributes:
label: Contact Details
description: How can we get in touch with you if we need more info?
placeholder: ex. email@example.com
validations:
required: false
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: version
attributes:
label: Version
description: What version of our software are you running? (use `--version` to get a version string)
placeholder: "cosmocc (GCC) 12.3.0"
validations:
required: true
- type: dropdown
id: operating-system
attributes:
label: What operating system are you seeing the problem on?
multiple: true
options:
- Linux
- Mac
- Windows
- FreeBSD
- OpenBSD
- NetBSD
- BIOS
validations:
required: false
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell

View file

@ -1,59 +0,0 @@
name: Medium Severity Bug
description: Used to report medium severity bugs in cosmopolitan (e.g. Malfunctioning Features but generally still useable)
title: "Bug: "
labels: ["bug", "medium severity"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Please include information about your system, the steps to reproduce the bug,
and the version of cosmopolitan that you are using.
If possible, please provide a minimal code example that reproduces the bug.
You may also consider using function call tracing `--ftrace` or the lighter system call tracing `--strace`
for additional technical logging that may allow us to narrow down where the fault occurred.
- type: input
id: contact
attributes:
label: Contact Details
description: How can we get in touch with you if we need more info?
placeholder: ex. email@example.com
validations:
required: false
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: version
attributes:
label: Version
description: What version of our software are you running? (use `--version` to get a version string)
placeholder: "cosmocc (GCC) 12.3.0"
validations:
required: true
- type: dropdown
id: operating-system
attributes:
label: What operating system are you seeing the problem on?
multiple: true
options:
- Linux
- Mac
- Windows
- FreeBSD
- OpenBSD
- NetBSD
- BIOS
validations:
required: false
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell

View file

@ -1,59 +0,0 @@
name: High Severity Bug
description: Used to report high severity bugs in cosmopolitan (e.g. Malfunctioning features hindering important common workflow)
title: "Bug: "
labels: ["bug", "high severity"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Please include information about your system, the steps to reproduce the bug,
and the version of cosmopolitan that you are using.
If possible, please provide a minimal code example that reproduces the bug.
You may also consider using function call tracing `--ftrace` or the lighter system call tracing `--strace`
for additional technical logging that may allow us to narrow down where the fault occurred.
- type: input
id: contact
attributes:
label: Contact Details
description: How can we get in touch with you if we need more info?
placeholder: ex. email@example.com
validations:
required: false
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: version
attributes:
label: Version
description: What version of our software are you running? (use `--version` to get a version string)
placeholder: "cosmocc (GCC) 12.3.0"
validations:
required: true
- type: dropdown
id: operating-system
attributes:
label: What operating system are you seeing the problem on?
multiple: true
options:
- Linux
- Mac
- Windows
- FreeBSD
- OpenBSD
- NetBSD
- BIOS
validations:
required: false
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell

View file

@ -1,59 +0,0 @@
name: Critical Severity Bug
description: Used to report critical severity bugs in cosmopolitan (e.g. Crashing, Corrupted, Dataloss)
title: "Bug: "
labels: ["bug", "critical severity"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Please include information about your system, the steps to reproduce the bug,
and the version of cosmopolitan that you are using.
If possible, please provide a minimal code example that reproduces the bug.
You may also consider using function call tracing `--ftrace` or the lighter system call tracing `--strace`
for additional technical logging that may allow us to narrow down where the fault occurred.
- type: input
id: contact
attributes:
label: Contact Details
description: How can we get in touch with you if we need more info?
placeholder: ex. email@example.com
validations:
required: false
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: version
attributes:
label: Version
description: What version of our software are you running? (use `--version` to get a version string)
placeholder: "cosmocc (GCC) 12.3.0"
validations:
required: true
- type: dropdown
id: operating-system
attributes:
label: What operating system are you seeing the problem on?
multiple: true
options:
- Linux
- Mac
- Windows
- FreeBSD
- OpenBSD
- NetBSD
- BIOS
validations:
required: false
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell

View file

@ -1,51 +0,0 @@
name: Enhancement template
description: Used to request enhancements for cosmopolitan
title: "Feature Request: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
[Please post your idea first in Discussion if there is not yet a consensus for this enhancement request. This will help to keep this issue tracker focused on enhancements that the community has agreed needs to be implemented.](https://github.com/jart/cosmopolitan/discussions/categories/ideas)
- type: checkboxes
id: prerequisites
attributes:
label: Prerequisites
description: Please confirm the following before submitting your enhancement request.
options:
- label: I am running the latest code. Mention the version if possible as well.
required: true
- label: I carefully followed the [README.md](https://github.com/jart/cosmopolitan/blob/master/README.md).
required: true
- label: I searched using keywords relevant to my issue to make sure that I am creating a new issue that is not already open (or closed).
required: true
- label: I reviewed the [Discussions](https://github.com/jart/cosmopolitan/discussions), and have a new and useful enhancement to share.
required: true
- type: textarea
id: feature-description
attributes:
label: Feature Description
description: Please provide a detailed written description of what you were trying to do, and what you expected `cosmopolitan` to do as an enhancement.
placeholder: Detailed description of the enhancement
validations:
required: true
- type: textarea
id: motivation
attributes:
label: Motivation
description: Please provide a detailed written description of reasons why this feature is necessary and how it is useful to `cosmopolitan` users.
placeholder: Explanation of why this feature is needed and its benefits
validations:
required: true
- type: textarea
id: possible-implementation
attributes:
label: Possible Implementation
description: If you have an idea as to how it can be implemented, please write a detailed description. Feel free to give links to external sources or share visuals that might be helpful to understand the details better.
placeholder: Detailed description of potential implementation
validations:
required: false

View file

@ -1,52 +0,0 @@
name: Research
description: Track new technical research area
title: "Research: "
labels: ["research"]
body:
- type: markdown
attributes:
value: |
Don't forget to check for any [duplicate research issue tickets](https://github.com/jart/cosmopolitan/issues?q=is%3Aopen+is%3Aissue+label%3A%22research+%F0%9F%94%AC%22)
- type: checkboxes
id: research-stage
attributes:
label: Research Stage
description: Track general state of this research ticket
options:
- label: Background Research (Let's try to avoid reinventing the wheel)
- label: Hypothesis Formed (How do you think this will work and it's effect?)
- label: Strategy / Implementation Forming
- label: Analysis of results
- label: Debrief / Documentation (So people in the future can learn from us)
- type: textarea
id: background
attributes:
label: Previous existing literature and research
description: Whats the current state of the art and whats the motivation for this research?
- type: textarea
id: hypothesis
attributes:
label: Hypothesis
description: How do you think this will work and it's effect?
- type: textarea
id: implementation
attributes:
label: Implementation
description: Got an approach? e.g. a PR ready to go?
- type: textarea
id: analysis
attributes:
label: Analysis
description: How does the proposed implementation behave?
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell

View file

@ -1,28 +0,0 @@
name: Refactor (Maintainers)
description: Used to track refactoring opportunities
title: "Refactor: "
labels: ["refactor"]
body:
- type: markdown
attributes:
value: |
Don't forget to [check for existing refactor issue tickets](https://github.com/jart/cosmopolitan/issues?q=is%3Aopen+is%3Aissue+label%3Arefactoring) in case it's already covered.
Also you may want to check [Pull request refactor label as well](https://github.com/jart/cosmopolitan/pulls?q=is%3Aopen+is%3Apr+label%3Arefactoring) for duplicates too.
- type: textarea
id: background-description
attributes:
label: Background Description
description: Please provide a detailed written description of the pain points you are trying to solve.
placeholder: Detailed description behind your motivation to request refactor
validations:
required: true
- type: textarea
id: possible-approaches
attributes:
label: Possible Refactor Approaches
description: If you have some idea of possible approaches to solve this problem. You may want to make it a todo list.
placeholder: Your idea of possible refactoring opportunity/approaches
validations:
required: false

View file

@ -1,15 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: FAQ
url: https://github.com/jart/cosmopolitan/wiki/FAQ
about: Is your question a common one? You may want to check here first.
- name: Got an idea?
url: https://github.com/jart/cosmopolitan/discussions/categories/ideas
about: Pop it there. It may then become an enhancement ticket.
- name: Got a question?
url: https://github.com/jart/cosmopolitan/discussions/categories/q-a
about: Ask a question there!
- name: Want to contribute?
url: https://github.com/jart/cosmopolitan/wiki/contribute
about: Head to the contribution guide page of the wiki for areas you can help with

49
.github/labeler.yml vendored
View file

@ -1,49 +0,0 @@
# https://github.com/actions/labeler
documentation:
- changed-files:
- any-glob-to-any-file:
- README.md
- LICENSE
- CONTRIBUTING.md
- libc/README.md
- tool/cosmocc/README.md
- third_party/getopt/README.txt
build:
- changed-files:
- any-glob-to-any-file:
- build/**
- Makefile
- '*/*.mk'
examples:
- changed-files:
- any-glob-to-any-file: examples/**
devops:
- changed-files:
- any-glob-to-any-file:
- .github/**
- .clang-format
dsp:
- changed-files:
- any-glob-to-any-file:
- dsp/**
ape:
- changed-files:
- any-glob-to-any-file:
- ape/**
libc:
- changed-files:
- any-glob-to-any-file:
- libc/**
net:
- changed-files:
- any-glob-to-any-file:
- net/**
third_party:
- changed-files:
- any-glob-to-any-file:
- third_party/**
tool:
- changed-files:
- any-glob-to-any-file:
- tool/**

View file

@ -1,8 +1,5 @@
name: build name: build
env:
COSMOCC_VERSION: 3.9.2
on: on:
push: push:
branches: branches:
@ -22,48 +19,13 @@ jobs:
matrix: matrix:
mode: ["", tiny, rel, tinylinux, optlinux] mode: ["", tiny, rel, tinylinux, optlinux]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with:
# Full checkout needed for git-restore-mtime-bare.
fetch-depth: 0
# TODO(jart): fork this action.
- uses: chetan/git-restore-mtime-action@v2
- uses: actions/cache/restore@v4
id: cache
with:
path: |
.cosmocc
o
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
restore-keys: |
${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-
${{ env.COSMOCC_VERSION }}-
- name: Restore mtimes
if: steps.cache.outputs.cache-hit == 'true'
run: |
while read mtime file; do
[ -f "$file" ] && touch -d "@$mtime" "$file"
done < o/.mtimes
- name: support ape bins 1 - name: support ape bins 1
run: sudo cp -a build/bootstrap/ape.elf /usr/bin/ape run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
- name: support ape bins 2 - name: support ape bins 2
run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
- name: make matrix - name: make matrix
run: V=0 make -j2 MODE=${{ matrix.mode }} run: V=0 make -j2 MODE=${{ matrix.mode }}
- name: Save mtimes
run: |
find o -type f -exec stat -c "%Y %n" {} \; > o/.mtimes
- uses: actions/cache/save@v4
with:
path: |
.cosmocc
o
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}

View file

@ -1,17 +0,0 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: "jart/cosmopolitan"
- uses: actions/labeler@v5
with:
configuration-path: '.github/labeler.yml'

View file

@ -1,24 +0,0 @@
name: Nightly cosmocc
on:
schedule:
# https://crontab.guru/#37_4_*_*_*
- cron: "37 4 * * *"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
build-cosmocc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
sudo cp build/bootstrap/ape.elf /usr/bin/ape
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
- run: tool/cosmocc/package.sh
# https://github.com/actions/upload-artifact/issues/590
- uses: actions/upload-artifact@v4.3.5
with:
name: cosmocc
path: cosmocc
compression-level: 9

4
.gitignore vendored
View file

@ -1,8 +1,7 @@
# -*- conf -*- # -*- conf -*-
/o /o
/cosmocc /.prompt.jtlp
/.cosmocc
# TODO: Find some way to have Python write to o/ # TODO: Find some way to have Python write to o/
__pycache__ __pycache__
@ -15,4 +14,3 @@ __pycache__
/tool/emacs/*.elc /tool/emacs/*.elc
/perf.data /perf.data
/perf.data.old /perf.data.old
/qemu*core

111
.vscode/c_cpp_properties.json vendored Normal file
View file

@ -0,0 +1,111 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}"
],
"cStandard": "c11",
"cppStandard": "c++11",
"forcedInclude": ["${workspaceFolder}/.vscode/vscode.h"],
"defines": [
"libcesque=",
"pureconst=",
"paramsnonnull(x)=",
"alignas(x)",
"alignof(x)",
"artificial=",
"dontdiscard=",
"mayalias=",
"forceinline=",
"forcealign(x)=",
"scanfesque(x)=",
"strftimeesque(x)=",
"wontreturn=",
"textreal=",
"mallocesque=",
"callocesque=",
"vallocesque=",
"reallocesque=",
"strlenesque=",
"memcpyesque=",
"hasatleast=",
"noinline=",
"textexit=",
"returnstwice=",
"textwindows=",
"privileged=",
"compatfn=",
"dontinstrument=",
"nodebuginfo=",
"interruptfn=",
"optimizespeed=",
"forcealignargpointer=",
"dontasan=",
"dontubsan=",
"donothing=",
"nosideeffect=",
"unreachable=",,
"notpossible=",
"thatispacked=",
"dontthrow=",
"nocallback=",
"relegated=",
"hidden=",
"textstartup=",
"initarray=",
"returnsnonnull=",
"returnspointerwithnoaliases=",
"printfesque(x)=",
"attributeallocsize(x)=",
"returnsaligned(x)=",
"attributeallocalign(x)=",
"nullterminated(x)="
]
},
{
"name": "Linux",
"includePath": [
"${workspaceFolder}"
],
"cStandard": "gnu17",
"compilerPath": "${workspaceFolder}/o/third_party/gcc/bin/x86_64-linux-musl-gcc",
"compilerArgs": [
"-Wall",
"-Werror",
"-fdebug-prefix-map=${workspaceFolder}=",
"-frecord-gcc-switches",
"-Wa,-W",
"-Wa,-I.",
"-Wa,--noexecstack",
"-Og",
"-g",
"-gdescribe-dies",
"-msse3",
"-mno-red-zone",
"-fno-math-errno",
"-fno-trapping-math",
"-fno-fp-int-builtin-inexact",
"-fno-ident",
"-fno-common",
"-fno-gnu-unique",
"-fstrict-aliasing",
"-fstrict-overflow",
"-fno-semantic-interposition",
"-mno-omit-leaf-frame-pointer",
"-fno-jump-tables",
"-nostdinc",
"-iquote."
],
"forcedInclude": [
"libc/integral/normalize.inc"
],
"defines": [
"COSMO",
"MODE=",
"IMAGE_BASE_VIRTUAL=0x400000"
]
}
],
"version": 4
}

36
.vscode/settings.json vendored
View file

@ -1,36 +0,0 @@
{
"C_Cpp.default.compilerPath": ".cosmocc/3.9.2/bin/aarch64-linux-cosmo-c++",
"C_Cpp.default.compilerArgs": [
"-nostdinc",
"-nostdlib",
"-iquote.",
"-isystemlibc/isystem",
"-isystemthird_party/libcxx",
"-includelibc/integral/normalize.inc",
"-D_COSMO_SOURCE",
"-D__aarch64__"
],
"[c]": {
"editor.tabSize": 2,
"editor.insertSpaces": true
},
"[cpp]": {
"editor.tabSize": 2,
"editor.insertSpaces": true
},
"[makefile]": {
"editor.tabSize": 8,
"editor.insertSpaces": false
},
"[make]": {
"editor.tabSize": 8,
"editor.insertSpaces": false
},
"[assembly]": {
"editor.tabSize": 8,
"editor.insertSpaces": true
},
"files.associations": {
"log.h": "c"
}
}

2
.vscode/vscode.h vendored Normal file
View file

@ -0,0 +1,2 @@
#define __VSCODE_INTELLISENSE__ 1
#include "libc/integral/normalize.inc"

View file

@ -2,77 +2,23 @@
We'd love to accept your patches! Please read this guide first. We'd love to accept your patches! Please read this guide first.
## Identity Disclosure
This project does not accept anonymous contributions. Justine Tunney
won't merge pull requests from strangers. In order to change the Cosmo
codebase, and have your changes be upstreamed, she has to know who you
are. You're encouraged to disclose your full name and email address to
the public too, by including them in your git commit messages; however
that's not a requirement; as we're happy to respect the wishes of
contributors who prefer to remain anonymous to the public.
## Copyright Assignment ## Copyright Assignment
The first time you send a pull request, you need to send an email to Please send an email to Justine Tunney <jtunney@gmail.com> stating that
Justine Tunney <jtunney@gmail.com> stating that you intend to assign her you intend to assign her the copyright to the changes you contribute to
the copyright to the changes you contribute to Cosmopolitan. It only Cosmopolitan. Please use the same email address you use for git commits
needs to happen once. This only applies to the code you *choose* to which should only contain original source code from you or other people
contribute. The email should be sent from an email address associated who are also assigning copyright. Please note that, if you're employed,
with your identity. Your email should link to your pull request. you may need to get your employer's approval beforehand. If you can not
assign copyright due to local laws, then you may alternatively consider
disclaiming it using the language in [Unlicense](https://unlicense.org)
or [CC-0](http://creativecommons.org/share-your-work/public-domain/cc0)
To make things easy, here's an example of a good email you can use: This is important because we can't produce 12kb single-file executables
that comply with license requirements if we have to embed lots of them.
> **From**: YOUR NAME (yname@gmail.com) Although that's less of an issue depending on the purpose of the files.
> **To**: Justine Tunney (jtunney@gmail.com) For example, ownership is much less of a concern in the unit test files
> **Subject**: Cosmopolitan Copyright Assignment for YOUR NAME so you're encouraged to put your copyright on those, provided it's ISC.
>
> Hi Justine,
>
> I made my first contribution to Cosmopolitan in
> https://github.com/jart/cosmopolitan/pull/XXXX could you please take a
> look? I intend to assign you the copyright to the changes I contribute
> to Cosmopolitan.
>
> Thanks!
Please note that in order to give Justine the copyright, it has to be
yours to give in the first place. If you're employed, then you should
get your employer's approval to do this beforehand. Even with big
companies like Google, this process is quick and painless. Usually we
see employers granting authorization in less than one day.
If you live in a country that doesn't recognize one's ability to assign
copyright, then you may alternatively consider disclaiming it using the
language in [Unlicense](https://unlicense.org) or
[CC-0](http://creativecommons.org/share-your-work/public-domain/cc0).
If you're checking-in third party code, then you need to have headers at
the top of each source file (but never header files) documenting its
owners and the code should go in the `third_party/` folder. Every third
party project should have a `README.cosmo` file that documents its
provenance as well as any local changes you've made.
## Copyright Policy Exceptions
### Tests
You're encoraged to claim ownership of your test code. If you add a new
file under the `test/` directory, then you should put your name in the
ISC license header at the top of the file. If you add new test cases to
an existing unit test file, then you're encouraged to append a line with
your name to the existing copyright header of that file.
### Exceptional Features
Let's say you discovered a faster better way to implement `log10()` and
you want to give it to Cosmopolitan. In cases like this, it really isn't
appropriate for Justine to own your code. What you could do instead, is
write your own new and improved `log10.c` from scratch, put your name on
the top with the ISC license, and then add a `__notice()` directive so
that your name will be embedded inside every executable that links the
`log10()` function. This will help you get your name out there. Please
note you need get approval from Justine each time you want to do this.
## Style Guide ## Style Guide
@ -83,4 +29,34 @@ clang-format -i -style=file tool/net/redbean.c
``` ```
If you use Emacs this can be automated on save for Cosmopolitan using If you use Emacs this can be automated on save for Cosmopolitan using
[tool/emacs/cosmo-format.el](tool/emacs/cosmo-format.el). [tool/emacs/cosmo-format.el]([tool/emacs/cosmo-format.el]).
### Source Files
- Must use include paths relative to the root of the repository
- Must have comment at top of file documenting copyright and license
- Must have notice embedding if not owned by Justine (exception: tests)
- May use language extensions that are supported by both GCC and Clang
- Should use Google indentation (otherwise use `/* clang-format off */`)
- Should use asm() instead of compiler APIs (exception: ctz, clz, memcpy)
### Header Files
- Must not have copyright or license comments
- Must have once guards (otherwise change `.h` to `.inc`)
- Must be ANSI C89 compatible to be included in the amalgamation header
- Must include its dependencies (exception: libc/integral/normalize.inc)
- Must not define objects (i.e. `cc -c -xc foo.h` will produce empty `.o`)
- Should not use typedefs
- Should not use forward declarations
- Should not include documentation comments
- Should not include parameter names in prototypes
- Should not pose problems if included by C++ or Assembly sources
- Should not declare non-ANSI code, at all, when the user requests ANSI
### Build Config
- Must not write files outside `o/`
- Must not communicate with Internet
- Must not depend on system libraries
- Must not depend on system commands (exception: sh, make, gzip, zip)

685
Makefile
View file

@ -1,5 +1,5 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ #-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set et ft=make ts=8 sw=8 fenc=utf-8 :vi ──────────────────────┘ #───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
# #
# SYNOPSIS # SYNOPSIS
# #
@ -21,23 +21,23 @@
# make -j8 -O MODE=tiny # make -j8 -O MODE=tiny
# #
# # build individual target # # build individual target
# make -j8 -O o//examples/hello # make -j8 -O o//examples/hello.com
# o//examples/hello # o//examples/hello.com
# #
# # view source # # view source
# less examples/hello.c # less examples/hello.c
# #
# # view binary # # view binary
# o//tool/viz/bing o//examples/hello | # o//tool/viz/bing.com o//examples/hello.com |
# o//tool/viz/fold # o//tool/viz/fold.com
# #
# # view transitive closure of legalese # # view transitive closure of legalese
# o//tool/viz/bing -n o//examples/hello | # o//tool/viz/bing.com -n o//examples/hello.com |
# o//tool/viz/fold # o//tool/viz/fold.com
# #
# # basic debugging # # basic debugging
# make -j8 -O MODE=dbg o/dbg/examples/crashreport # make -j8 -O MODE=dbg o/dbg/examples/crashreport.com
# o/examples/crashreport # o/examples/crashreport.com
# less examples/crashreport.c # less examples/crashreport.c
# #
# # extremely tiny binaries # # extremely tiny binaries
@ -51,7 +51,7 @@
# #
# TROUBLESHOOTING # TROUBLESHOOTING
# #
# make -j8 -O V=1 o//examples/hello # make -j8 -O V=1 o//examples/hello.com
# make o//examples/life.elf -pn |& less # make o//examples/life.elf -pn |& less
# etc. # etc.
# #
@ -59,7 +59,7 @@
# #
# build/config.mk # build/config.mk
SHELL = build/bootstrap/cocmd SHELL = build/bootstrap/cocmd.com
MAKEFLAGS += --no-builtin-rules MAKEFLAGS += --no-builtin-rules
.SUFFIXES: .SUFFIXES:
@ -73,110 +73,11 @@ MODE := $(m)
endif endif
endif endif
COMMA := ,
PWD := $(shell pwd)
# detect wsl2 running cosmopolitan binaries on the host by checking whether:
# - user ran .cosmocc/current/bin/make, in which case make's working directory
# is in wsl
# - user ran make, in which case cocmd's working directory is in wsl
ifneq ($(findstring //wsl.localhost/,$(CURDIR) $(PWD)),)
$(warning wsl2 interop is enabled)
$(error you need to run sudo sh -c 'echo -1 > /proc/sys/fs/binfmt_misc/WSLInterop')
endif
UNAME_M := $(shell uname -m)
UNAME_S := $(shell uname -s)
# apple still distributes a 17 year old version of gnu make
ifeq ($(MAKE_VERSION), 3.81)
$(error please use https://cosmo.zip/pub/cosmos/bin/make)
endif
LC_ALL = C
SOURCE_DATE_EPOCH = 0
ARFLAGS = rcsD
ZFLAGS ?=
XARGS ?= xargs -P4 -rs8000
DOT ?= dot
CLANG = clang
TMPDIR = o/tmp
AR = $(BOOTSTRAP)/ar.ape
CP = $(BOOTSTRAP)/cp.ape
RM = $(BOOTSTRAP)/rm.ape -f
GZIP = $(BOOTSTRAP)/gzip.ape
ECHO = $(BOOTSTRAP)/echo.ape
CHMOD = $(BOOTSTRAP)/chmod.ape
TOUCH = $(BOOTSTRAP)/touch.ape
PKG = $(BOOTSTRAP)/package.ape
MKDEPS = $(BOOTSTRAP)/mkdeps
ZIPOBJ = $(BOOTSTRAP)/zipobj
ZIPCOPY = $(BOOTSTRAP)/zipcopy
PECHECK = $(BOOTSTRAP)/pecheck
FIXUPOBJ = $(BOOTSTRAP)/fixupobj
OBJBINCOPY = $(BOOTSTRAP)/objbincopy
MKDIR = $(BOOTSTRAP)/mkdir.ape -p
COMPILE = $(BOOTSTRAP)/compile.ape -V9 -M2048m -P8192 $(QUOTA)
# the default build modes is empty string
# on x86_64 hosts, MODE= is the same as MODE=x86_64
# on aarch64 hosts, MODE= is changed to MODE=aarch64
ifeq ($(MODE),)
ifeq ($(UNAME_M),arm64)
MODE := aarch64
endif
ifeq ($(UNAME_M),aarch64)
MODE := aarch64
endif
endif
ifneq ($(findstring aarch64,$(MODE)),)
ARCH = aarch64
HOSTS ?= pi pi5 studio freebsdarm
else
ARCH = x86_64
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10 luna
endif
ZIPOBJ_FLAGS += -a$(ARCH)
export ADDR2LINE
export LC_ALL
export MKDIR
export MODE
export SOURCE_DATE_EPOCH
export TMPDIR
COSMOCC = .cosmocc/3.9.2
BOOTSTRAP = $(COSMOCC)/bin
TOOLCHAIN = $(COSMOCC)/bin/$(ARCH)-linux-cosmo-
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.9.2 f4ff13af65fcd309f3f1cfd04275996fb7f72a4897726628a8c9cf732e850193)
IGNORE := $(shell $(MKDIR) $(TMPDIR))
AS = $(TOOLCHAIN)as
CC = $(TOOLCHAIN)gcc
CXX = $(TOOLCHAIN)g++
CXXFILT = $(TOOLCHAIN)c++filt
LD = $(TOOLCHAIN)ld.bfd
NM = $(TOOLCHAIN)nm
GCC = $(TOOLCHAIN)gcc
STRIP = $(TOOLCHAIN)strip
OBJCOPY = $(TOOLCHAIN)objcopy
OBJDUMP = $(TOOLCHAIN)objdump
ifneq ($(wildcard $(PWD)/$(TOOLCHAIN)addr2line),)
ADDR2LINE = $(PWD)/$(TOOLCHAIN)addr2line
else
ADDR2LINE = $(TOOLCHAIN)addr2line
endif
# primary build rules
all: o all: o
o: o/$(MODE) o: o/$(MODE)
o/$(MODE): \ o/$(MODE): \
o/$(MODE)/ape \ o/$(MODE)/ape \
o/$(MODE)/ctl \
o/$(MODE)/dsp \ o/$(MODE)/dsp \
o/$(MODE)/net \ o/$(MODE)/net \
o/$(MODE)/libc \ o/$(MODE)/libc \
@ -185,43 +86,31 @@ o/$(MODE): \
o/$(MODE)/examples \ o/$(MODE)/examples \
o/$(MODE)/third_party o/$(MODE)/third_party
# TODO(jart): Make Emacs `C-c C-c` shortcut not need this.
.PHONY: o/$(MODE)/ o/$(MODE)/.
o/$(MODE)/: o/$(MODE)
o/$(MODE)/.: o/$(MODE)
# check if we're using o//third_party/make/make
# we added sandboxing to guarantee cosmo's makefile is hermetic
# it also shaves away 200ms of startup latency with native $(uniq)
ifneq ($(LANDLOCKMAKE_VERSION),) ifneq ($(LANDLOCKMAKE_VERSION),)
ifeq ($(UNAME_S),Linux)
ifeq ($(wildcard /usr/bin/ape),) ifeq ($(wildcard /usr/bin/ape),)
$(warning please run ape/apeinstall.sh if you intend to use landlock make) $(warning please run ape/apeinstall.sh if you intend to use landlock make)
$(shell sleep .5) $(shell sleep .5)
endif endif
endif ifeq ($(USE_SYSTEM_TOOLCHAIN),)
ifneq ($(TOOLCHAIN),)
.STRICT = 1 .STRICT = 1
endif endif
endif endif
.PLEDGE += stdio rpath wpath cpath fattr proc .PLEDGE = stdio rpath wpath cpath fattr proc
.UNVEIL += \ .UNVEIL = \
libc/integral \ libc/integral \
libc/stdbool.h \ libc/disclaimer.inc \
rwc:/dev/shm \ rwc:/dev/shm \
rx:.cosmocc \ rx:build/bootstrap \
rx:build/bootstrap \ rx:o/third_party/gcc \
r:build/portcosmo.h \ r:build/portcosmo.h \
/proc/stat \ /proc/stat \
rw:/dev/null \ rw:/dev/null \
rw:/dev/full \ rw:/dev/full \
w:o/stack.log \ w:o/stack.log \
/etc/hosts \ /etc/hosts \
~/.runit.psk \ ~/.runit.psk \
/proc/self/status \ /proc/self/status \
rx:/usr/bin/qemu-aarch64 \
rx:o/third_party/qemu/qemu-aarch64 \
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
PKGS = PKGS =
@ -232,163 +121,149 @@ include build/definitions.mk # ├──META
include build/config.mk # │ You can build include build/config.mk # │ You can build
include build/rules.mk # │ You can topologically order include build/rules.mk # │ You can topologically order
include build/online.mk #─┘ include build/online.mk #─┘
include libc/nexgen32e/BUILD.mk #─┐ include libc/nexgen32e/nexgen32e.mk #─┐
include libc/sysv/BUILD.mk # ├──SYSTEM SUPPORT include libc/sysv/sysv.mk # ├──SYSTEM SUPPORT
include libc/nt/BUILD.mk # │ You can do math include libc/nt/nt.mk # │ You can do math
include libc/intrin/BUILD.mk # │ You can use the stack include libc/intrin/intrin.mk # │ You can use the stack
include third_party/compiler_rt/BUILD.mk # │ You can manipulate arrays include third_party/compiler_rt/compiler_rt.mk # │ You can manipulate arrays
include libc/tinymath/BUILD.mk # │ You can issue raw system calls include libc/tinymath/tinymath.mk # │ You can issue raw system calls
include libc/str/BUILD.mk # │ include libc/str/str.mk # │
include third_party/xed/BUILD.mk # │ include third_party/xed/xed.mk # │
include third_party/puff/BUILD.mk # │ include third_party/puff/puff.mk # │
include libc/elf/BUILD.mk # │ include third_party/zlib/zlib.mk # │
include ape/BUILD.mk # │ include third_party/double-conversion/dc.mk # │
include libc/fmt/BUILD.mk # │ include libc/elf/elf.mk # │
include libc/vga/BUILD.mk # │ include ape/ape.mk # │
include libc/irq/BUILD.mk #─┘ include libc/fmt/fmt.mk # │
include libc/calls/BUILD.mk #─┐ include libc/vga/vga.mk #─┘
include libc/irq/BUILD.mk # ├──SYSTEMS RUNTIME include libc/calls/calls.mk #─┐
include third_party/nsync/BUILD.mk # │ You can issue system calls include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
include libc/runtime/BUILD.mk # │ include libc/crt/crt.mk # │ You can issue system calls
include libc/crt/BUILD.mk # │ include tool/hello/hello.mk # │
include third_party/dlmalloc/BUILD.mk #─┘ include third_party/nsync/nsync.mk # │
include libc/mem/BUILD.mk #─┐ include third_party/dlmalloc/dlmalloc.mk #─┘
include third_party/gdtoa/BUILD.mk # ├──DYNAMIC RUNTIME include libc/mem/mem.mk #─┐
include third_party/nsync/mem/BUILD.mk # │ You can now use stdio include third_party/gdtoa/gdtoa.mk # ├──DYNAMIC RUNTIME
include libc/proc/BUILD.mk # │ You can now use threads include third_party/nsync/mem/mem.mk # │ You can now use stdio
include libc/dlopen/BUILD.mk # │ You can now use processes include libc/thread/thread.mk # │ You can finally call malloc()
include libc/thread/BUILD.mk # │ You can finally call malloc() include libc/zipos/zipos.mk # │
include third_party/zlib/BUILD.mk # │ include libc/stdio/stdio.mk # │
include libc/stdio/BUILD.mk # │ include libc/time/time.mk # │
include tool/hello/BUILD.mk # │ include net/net.mk # │
include third_party/tz/BUILD.mk # │ include third_party/vqsort/vqsort.mk # │
include net/BUILD.mk # │ include libc/log/log.mk # │
include third_party/vqsort/BUILD.mk # │ include third_party/getopt/getopt.mk # │
include libc/log/BUILD.mk # │ include third_party/bzip2/bzip2.mk # │
include third_party/getopt/BUILD.mk # │ include dsp/core/core.mk # │
include third_party/bzip2/BUILD.mk # │ include third_party/musl/musl.mk # │
include dsp/core/BUILD.mk # │ include libc/x/x.mk # │
include third_party/zlib/gz/BUILD.mk # │ include third_party/stb/stb.mk # │
include third_party/intel/BUILD.mk # │ include dsp/scale/scale.mk # │
include third_party/aarch64/BUILD.mk # │ include dsp/mpeg/mpeg.mk # │
include libc/BUILD.mk #─┘ include dsp/dsp.mk # │
include libc/sock/BUILD.mk #─┐ include third_party/zlib/gz/gz.mk # │
include net/http/BUILD.mk # ├──ONLINE RUNTIME include third_party/intel/intel.mk # │
include third_party/musl/BUILD.mk # │ You can communicate with the network include third_party/aarch64/aarch64.mk # │
include third_party/regex/BUILD.mk # │ include libc/libc.mk #─┘
include third_party/tr/BUILD.mk # │ include libc/sock/sock.mk #─┐
include third_party/sed/BUILD.mk # │ include dsp/tty/tty.mk # ├──ONLINE RUNTIME
include libc/system/BUILD.mk # │ include libc/dns/dns.mk # │ You can communicate with the network
include libc/x/BUILD.mk # │ include net/http/http.mk # │
include dsp/scale/BUILD.mk # │ include third_party/mbedtls/mbedtls.mk # │
include dsp/mpeg/BUILD.mk # │ include third_party/libcxx/libcxx.mk # │
include dsp/tty/BUILD.mk # │ include third_party/ggml/ggml.mk # │
include dsp/audio/BUILD.mk # │ include third_party/radpajama/radpajama.mk # │
include dsp/prog/BUILD.mk # │ include net/https/https.mk # │
include dsp/BUILD.mk # │ include third_party/regex/regex.mk #─┘
include third_party/stb/BUILD.mk # │ include third_party/tidy/tidy.mk
include third_party/mbedtls/BUILD.mk # │ include third_party/third_party.mk
include third_party/ncurses/BUILD.mk # │ include third_party/nsync/testing/testing.mk
include third_party/readline/BUILD.mk # │ include libc/testlib/testlib.mk
include third_party/libunwind/BUILD.mk # | include tool/viz/lib/vizlib.mk
include third_party/libcxxabi/BUILD.mk # | include tool/args/args.mk
include third_party/double-conversion/BUILD.mk # │ include test/tool/args/test.mk
include ctl/BUILD.mk # │ include third_party/linenoise/linenoise.mk
include third_party/libcxx/BUILD.mk # │ include third_party/maxmind/maxmind.mk
include third_party/openmp/BUILD.mk # │ include net/finger/finger.mk
include third_party/pcre/BUILD.mk # │ include third_party/double-conversion/test/test.mk
include third_party/less/BUILD.mk # │ include third_party/lua/lua.mk
include net/https/BUILD.mk #─┘ include third_party/zstd/zstd.mk
include third_party/tidy/BUILD.mk include third_party/tr/tr.mk
include third_party/BUILD.mk include third_party/sed/sed.mk
include third_party/nsync/testing/BUILD.mk include third_party/awk/awk.mk
include libc/testlib/BUILD.mk include third_party/hiredis/hiredis.mk
include tool/viz/lib/BUILD.mk include third_party/make/make.mk
include tool/args/BUILD.mk include third_party/ctags/ctags.mk
include test/math/BUILD.mk include third_party/finger/finger.mk
include test/posix/BUILD.mk include third_party/argon2/argon2.mk
include test/ctl/BUILD.mk include third_party/smallz4/smallz4.mk
include test/libcxx/BUILD.mk include third_party/sqlite3/sqlite3.mk
include test/tool/args/BUILD.mk include third_party/mbedtls/test/test.mk
include third_party/linenoise/BUILD.mk include third_party/quickjs/quickjs.mk
include third_party/maxmind/BUILD.mk include third_party/lz4cli/lz4cli.mk
include net/finger/BUILD.mk include third_party/zip/zip.mk
include third_party/double-conversion/test/BUILD.mk include third_party/xxhash/xxhash.mk
include third_party/lua/BUILD.mk include third_party/unzip/unzip.mk
include third_party/tree/BUILD.mk include tool/build/lib/buildlib.mk
include third_party/zstd/BUILD.mk include third_party/chibicc/chibicc.mk
include third_party/awk/BUILD.mk include third_party/chibicc/test/test.mk
include third_party/hiredis/BUILD.mk include third_party/python/python.mk
include third_party/make/BUILD.mk include tool/build/build.mk
include third_party/ctags/BUILD.mk include tool/curl/curl.mk
include third_party/finger/BUILD.mk include tool/ape/ape.mk
include third_party/argon2/BUILD.mk include third_party/qemu/qemu.mk
include third_party/smallz4/BUILD.mk include examples/examples.mk
include third_party/sqlite3/BUILD.mk include examples/pyapp/pyapp.mk
include third_party/mbedtls/test/BUILD.mk include examples/pylife/pylife.mk
include third_party/lz4cli/BUILD.mk include tool/decode/lib/decodelib.mk
include third_party/zip/BUILD.mk include tool/decode/decode.mk
include third_party/xxhash/BUILD.mk include tool/lambda/lib/lib.mk
include third_party/unzip/BUILD.mk include tool/lambda/lambda.mk
include tool/build/lib/BUILD.mk include tool/plinko/lib/lib.mk
include third_party/chibicc/BUILD.mk include tool/plinko/plinko.mk
include third_party/chibicc/test/BUILD.mk include test/tool/plinko/test.mk
include third_party/python/BUILD.mk include tool/net/net.mk
include tool/build/BUILD.mk include tool/viz/viz.mk
include tool/curl/BUILD.mk include tool/tool.mk
include third_party/qemu/BUILD.mk include net/turfwar/turfwar.mk
include third_party/libcxxabi/test/BUILD.mk include test/libc/tinymath/test.mk
include examples/BUILD.mk include test/libc/intrin/test.mk
include examples/pyapp/BUILD.mk include test/libc/mem/test.mk
include examples/pylife/BUILD.mk include test/libc/nexgen32e/test.mk
include tool/decode/lib/BUILD.mk include test/libc/runtime/test.mk
include tool/decode/BUILD.mk include test/libc/thread/test.mk
include tool/lambda/lib/BUILD.mk include test/libc/sock/test.mk
include tool/lambda/BUILD.mk include test/libc/str/test.mk
include tool/plinko/lib/BUILD.mk include test/libc/log/test.mk
include tool/plinko/BUILD.mk include test/libc/str/test.mk
include test/tool/plinko/BUILD.mk include test/libc/calls/test.mk
include tool/net/BUILD.mk include test/libc/x/test.mk
include tool/viz/BUILD.mk include test/libc/xed/test.mk
include tool/BUILD.mk include test/libc/fmt/test.mk
include net/turfwar/BUILD.mk include test/libc/dns/test.mk
include test/libc/tinymath/BUILD.mk include test/libc/time/test.mk
include test/libc/intrin/BUILD.mk include test/libc/stdio/test.mk
include test/libc/mem/BUILD.mk include test/libc/zipos/test.mk
include test/libc/nexgen32e/BUILD.mk include test/libc/release/test.mk
include test/libc/runtime/BUILD.mk include test/libc/test.mk
include test/libc/thread/BUILD.mk include test/net/http/test.mk
include test/libc/sock/BUILD.mk include test/net/https/test.mk
include test/libc/str/BUILD.mk include test/net/finger/test.mk
include test/libc/log/BUILD.mk include test/net/test.mk
include test/libc/str/BUILD.mk include test/tool/build/lib/test.mk
include test/libc/calls/BUILD.mk include test/tool/build/test.mk
include test/libc/x/BUILD.mk include test/tool/viz/lib/test.mk
include test/libc/xed/BUILD.mk include test/tool/viz/test.mk
include test/libc/fmt/BUILD.mk include test/tool/net/test.mk
include test/libc/time/BUILD.mk include test/tool/test.mk
include test/libc/proc/BUILD.mk include test/dsp/core/test.mk
include test/libc/stdio/BUILD.mk include test/dsp/scale/test.mk
include test/libc/system/BUILD.mk include test/dsp/tty/test.mk
include test/libc/BUILD.mk include test/dsp/test.mk
include test/net/http/BUILD.mk include examples/package/lib/build.mk
include test/net/https/BUILD.mk include examples/package/build.mk
include test/net/finger/BUILD.mk
include test/net/BUILD.mk
include test/tool/build/lib/BUILD.mk
include test/tool/build/BUILD.mk
include test/tool/viz/lib/BUILD.mk
include test/tool/viz/BUILD.mk
include test/tool/net/BUILD.mk
include test/tool/BUILD.mk
include test/dsp/core/BUILD.mk
include test/dsp/scale/BUILD.mk
include test/dsp/tty/BUILD.mk
include test/dsp/BUILD.mk
include examples/package/lib/BUILD.mk
include examples/package/BUILD.mk
#-φ-examples/package/new.sh #-φ-examples/package/new.sh
include test/BUILD.mk include test/test.mk
OBJS = $(foreach x,$(PKGS),$($(x)_OBJS)) OBJS = $(foreach x,$(PKGS),$($(x)_OBJS))
SRCS := $(foreach x,$(PKGS),$($(x)_SRCS)) SRCS := $(foreach x,$(PKGS),$($(x)_SRCS))
@ -414,7 +289,7 @@ o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(
o/$(MODE)/incs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(INCS) $(INCS),$(dir $(x)))) $(INCS) $(INCS) o/$(MODE)/incs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(INCS) $(INCS),$(dir $(x)))) $(INCS) $(INCS)
$(file >$@,$(INCS)) $(file >$@,$(INCS))
o/$(MODE)/depend: o/$(MODE)/.x o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt o/$(MODE)/incs.txt $(SRCS) $(HDRS) $(INCS) o/$(MODE)/depend: o/$(MODE)/.x o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt o/$(MODE)/incs.txt $(SRCS) $(HDRS) $(INCS)
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt $(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -r o/$(MODE)/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
o/$(MODE)/srcs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x)))) o/$(MODE)/srcs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x))))
$(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x))) $(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x)))
@ -422,87 +297,76 @@ o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS
$(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x))) $(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x)))
TAGS: private .UNSANDBOXED = 1 TAGS: private .UNSANDBOXED = 1
TAGS: o/$(MODE)/srcs-old.txt $(SRCS) #o/$(MODE)/third_party/ctags/ctags TAGS: o/$(MODE)/srcs-old.txt $(SRCS) #o/$(MODE)/third_party/ctags/ctags.com
@$(RM) $@ @$(RM) $@
@o/$(MODE)/third_party/ctags/ctags $(TAGSFLAGS) -L $< -o $@ @o/$(MODE)/third_party/ctags/ctags.com $(TAGSFLAGS) -L $< -o $@
HTAGS: private .UNSANDBOXED = 1 HTAGS: private .UNSANDBOXED = 1
HTAGS: o/$(MODE)/hdrs-old.txt $(filter-out third_party/libcxx/%,$(HDRS)) #o/$(MODE)/third_party/ctags/ctags HTAGS: o/$(MODE)/hdrs-old.txt $(filter-out third_party/libcxx/%,$(HDRS)) #o/$(MODE)/third_party/ctags/ctags.com
@$(RM) $@ @$(RM) $@
@build/htags o/$(MODE)/third_party/ctags/ctags -L $< -o $@ @build/htags o/$(MODE)/third_party/ctags/ctags.com -L $< -o $@
loc: private .UNSANDBOXED = 1 loc: private .UNSANDBOXED = 1
loc: o/$(MODE)/tool/build/summy loc: o/$(MODE)/tool/build/summy.com
find -name \*.h -or -name \*.hpp -or -name \*.c -or -name \*.cc -or -name \*.cpp -or -name \*.S -or -name \*.mk | \ find -name \*.h -or -name \*.c -or -name \*.S | \
$(XARGS) wc -l | grep total | awk '{print $$1}' | $< $(XARGS) wc -l | grep total | awk '{print $$1}' | $<
COSMOPOLITAN = \ # PLEASE: MAINTAIN TOPOLOGICAL ORDER
CTL \ # FROM HIGHEST LEVEL TO LOWEST LEVEL
DSP_AUDIO \ COSMOPOLITAN_OBJECTS = \
LIBC_CALLS \
LIBC_DLOPEN \
LIBC_ELF \
LIBC_FMT \
LIBC_INTRIN \
LIBC_IRQ \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \
LIBC_NT_BCRYPTPRIMITIVES \
LIBC_NT_COMDLG32 \
LIBC_NT_GDI32 \
LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_PDH \
LIBC_NT_POWRPROF \
LIBC_NT_PSAPI \
LIBC_NT_REALTIME \
LIBC_NT_SHELL32 \
LIBC_NT_SYNCHRONIZATION \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
LIBC_PROC \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_THREAD \
LIBC_TINYMATH \
LIBC_VGA \
LIBC_X \
NET_HTTP \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_MUSL \
THIRD_PARTY_NSYNC \
THIRD_PARTY_NSYNC_MEM \
THIRD_PARTY_OPENMP \
THIRD_PARTY_PUFF \
THIRD_PARTY_REGEX \
THIRD_PARTY_TZ \
THIRD_PARTY_XED \
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ \
TOOL_ARGS \ TOOL_ARGS \
NET_HTTP \
LIBC_DNS \
LIBC_SOCK \
LIBC_NT_WS2_32 \
LIBC_NT_IPHLPAPI \
LIBC_NT_MSWSOCK \
LIBC_X \
THIRD_PARTY_GETOPT \
LIBC_LOG \
LIBC_TIME \
LIBC_ZIPOS \
THIRD_PARTY_MUSL \
LIBC_STDIO \
THIRD_PARTY_GDTOA \
THIRD_PARTY_REGEX \
LIBC_THREAD \
THIRD_PARTY_NSYNC_MEM \
LIBC_MEM \
THIRD_PARTY_DLMALLOC \
LIBC_RUNTIME \
THIRD_PARTY_NSYNC \
LIBC_ELF \
LIBC_CALLS \
LIBC_SYSV_CALLS \
LIBC_VGA \
LIBC_NT_PSAPI \
LIBC_NT_POWRPROF \
LIBC_NT_PDH \
LIBC_NT_GDI32 \
LIBC_NT_COMDLG32 \
LIBC_NT_USER32 \
LIBC_NT_NTDLL \
LIBC_NT_ADVAPI32 \
LIBC_NT_SYNCHRONIZATION \
LIBC_FMT \
THIRD_PARTY_PUFF \
THIRD_PARTY_COMPILER_RT \
LIBC_TINYMATH \
THIRD_PARTY_XED \
LIBC_STR \
LIBC_SYSV \
LIBC_INTRIN \
LIBC_NT_KERNEL32 \
LIBC_NEXGEN32E
COSMOPOLITAN_H_PKGS = \ COSMOPOLITAN_HEADERS = \
APE \ APE \
DSP_AUDIO \
LIBC \ LIBC \
LIBC_CALLS \ LIBC_CALLS \
LIBC_DNS \
LIBC_ELF \ LIBC_ELF \
LIBC_FMT \ LIBC_FMT \
LIBC_DLOPEN \
LIBC_INTRIN \ LIBC_INTRIN \
LIBC_LOG \ LIBC_LOG \
LIBC_MEM \ LIBC_MEM \
@ -511,14 +375,15 @@ COSMOPOLITAN_H_PKGS = \
LIBC_RUNTIME \ LIBC_RUNTIME \
LIBC_SOCK \ LIBC_SOCK \
LIBC_STDIO \ LIBC_STDIO \
LIBC_PROC \
THIRD_PARTY_NSYNC \ THIRD_PARTY_NSYNC \
THIRD_PARTY_XED \ THIRD_PARTY_XED \
LIBC_STR \ LIBC_STR \
LIBC_SYSV \ LIBC_SYSV \
LIBC_THREAD \ LIBC_THREAD \
LIBC_TIME \
LIBC_TINYMATH \ LIBC_TINYMATH \
LIBC_X \ LIBC_X \
LIBC_ZIPOS \
LIBC_VGA \ LIBC_VGA \
NET_HTTP \ NET_HTTP \
TOOL_ARGS \ TOOL_ARGS \
@ -526,43 +391,28 @@ COSMOPOLITAN_H_PKGS = \
THIRD_PARTY_GDTOA \ THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \ THIRD_PARTY_GETOPT \
THIRD_PARTY_MUSL \ THIRD_PARTY_MUSL \
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ \
THIRD_PARTY_REGEX THIRD_PARTY_REGEX
COSMOCC_PKGS = \
$(COSMOPOLITAN_H_PKGS) \
CTL \
THIRD_PARTY_AARCH64 \
THIRD_PARTY_LIBCXX \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
THIRD_PARTY_OPENMP \
THIRD_PARTY_INTEL
o/$(MODE)/cosmopolitan.a: \ o/$(MODE)/cosmopolitan.a: \
$(call reverse,$(call uniq,$(foreach x,$(COSMOPOLITAN),$($(x))))) $(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_A_OBJS))
COSMOCC_HDRS = \ o/cosmopolitan.h: \
$(wildcard libc/integral/*) \ o/$(MODE)/tool/build/rollup.com \
$(foreach x,$(COSMOCC_PKGS),$($(x)_HDRS)) \ libc/integral/normalize.inc \
$(foreach x,$(COSMOCC_PKGS),$($(x)_INCS)) $(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS)) \
$(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_INCS))
o/cosmocc.h.txt: Makefile libc $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x)))) $(HDRS) $(INCS) $(file >$(TMPDIR)/$(subst /,_,$@),libc/integral/normalize.inc $(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS)))
$(file >$@, $(call uniq,$(COSMOCC_HDRS))) @$(ECHO) '#define COSMO' >$@
@$(COMPILE) -AROLLUP -T$@ o/$(MODE)/tool/build/rollup.com @$(TMPDIR)/$(subst /,_,$@) >>$@
COSMOPOLITAN_H_ROOT_HDRS = \
libc/integral/normalize.inc \
$(foreach x,$(COSMOPOLITAN_H_PKGS),$($(x)_HDRS))
o/cosmopolitan.html: private .UNSANDBOXED = 1 o/cosmopolitan.html: private .UNSANDBOXED = 1
o/cosmopolitan.html: \ o/cosmopolitan.html: \
o/$(MODE)/third_party/chibicc/chibicc.dbg \ o/$(MODE)/third_party/chibicc/chibicc.com.dbg \
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))) \ $(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))) \
$(filter-out %.cpp,$(filter-out %.cc,$(SRCS))) \ $(SRCS) \
$(HDRS) $(HDRS)
$(file >$(TMPDIR)/$(subst /,_,$@),$(filter-out %.cpp,$(filter-out %.cc,$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))))) $(file >$(TMPDIR)/$(subst /,_,$@),$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))))
o/$(MODE)/third_party/chibicc/chibicc.dbg -J \ o/$(MODE)/third_party/chibicc/chibicc.com.dbg -J \
-fno-common -include libc/integral/normalize.inc -o $@ \ -fno-common -include libc/integral/normalize.inc -o $@ \
-DCOSMO @$(TMPDIR)/$(subst /,_,$@) -DCOSMO @$(TMPDIR)/$(subst /,_,$@)
@ -574,41 +424,26 @@ $(SRCS): \
libc/integral/lp64arg.inc \ libc/integral/lp64arg.inc \
libc/integral/lp64.inc libc/integral/lp64.inc
ifeq ($(ARCH), x86_64)
TOOLCHAIN_ARTIFACTS = \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/libc/crt/crt.o \
o/$(MODE)/ape/ape.elf \
o/$(MODE)/ape/ape.o \
o/$(MODE)/ape/ape-copy-self.o \
o/$(MODE)/ape/ape-no-modify-self.o \
o/$(MODE)/cosmopolitan.a \
o/$(MODE)/third_party/libcxx/libcxx.a \
o/$(MODE)/tool/build/march-native \
o/$(MODE)/tool/build/ar \
o/$(MODE)/tool/build/mktemper \
o/$(MODE)/tool/build/fixupobj \
o/$(MODE)/tool/build/zipcopy \
o/$(MODE)/tool/build/apelink \
o/$(MODE)/tool/build/pecheck
else
TOOLCHAIN_ARTIFACTS = \
o/$(MODE)/ape/ape.elf \
o/$(MODE)/ape/aarch64.lds \
o/$(MODE)/libc/crt/crt.o \
o/$(MODE)/cosmopolitan.a \
o/$(MODE)/third_party/libcxx/libcxx.a \
o/$(MODE)/tool/build/march-native \
o/$(MODE)/tool/build/fixupobj \
o/$(MODE)/tool/build/zipcopy
endif
.PHONY: toolchain .PHONY: toolchain
toolchain: $(TOOLCHAIN_ARTIFACTS) ifeq ($(ARCH), x86_64)
toolchain: o/cosmopolitan.h \
.PHONY: clean_toolchain o/$(MODE)/ape/public/ape.lds \
clean_toolchain: o/$(MODE)/libc/crt/crt.o \
$(RM) $(TOOLCHAIN_ARTIFACTS) o/$(MODE)/ape/ape.o \
o/$(MODE)/ape/ape-copy-self.o \
o/$(MODE)/ape/ape-no-modify-self.o \
o/$(MODE)/cosmopolitan.a \
o/$(MODE)/third_party/libcxx/libcxx.a \
o/$(MODE)/tool/build/fixupobj.com \
o/$(MODE)/tool/build/zipcopy.com
else
toolchain: o/$(MODE)/ape/aarch64.lds \
o/$(MODE)/libc/crt/crt.o \
o/$(MODE)/cosmopolitan.a \
o/$(MODE)/third_party/libcxx/libcxx.a \
o/$(MODE)/tool/build/fixupobj.com \
o/$(MODE)/tool/build/zipcopy.com
endif
aarch64: private .INTERNET = true aarch64: private .INTERNET = true
aarch64: private .UNSANDBOXED = true aarch64: private .UNSANDBOXED = true

422
README.md
View file

@ -3,49 +3,75 @@
[![build](https://github.com/jart/cosmopolitan/actions/workflows/build.yml/badge.svg)](https://github.com/jart/cosmopolitan/actions/workflows/build.yml) [![build](https://github.com/jart/cosmopolitan/actions/workflows/build.yml/badge.svg)](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
# Cosmopolitan # Cosmopolitan
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C/C++ [Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C
a build-once run-anywhere language, like Java, except it doesn't need an a build-once run-anywhere language, like Java, except it doesn't need an
interpreter or virtual machine. Instead, it reconfigures stock GCC and interpreter or virtual machine. Instead, it reconfigures stock GCC and
Clang to output a POSIX-approved polyglot format that runs natively on Clang to output a POSIX-approved polyglot format that runs natively on
Linux + Mac + Windows + FreeBSD + OpenBSD 7.3 + NetBSD + BIOS with the Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS with the best
best possible performance and the tiniest footprint imaginable. possible performance and the tiniest footprint imaginable.
## Background ## Background
For an introduction to this project, please read the [actually portable For an introduction to this project, please read the [αcτµαlly pδrταblε
executable](https://justine.lol/ape.html) blog post and [cosmopolitan εxεcµταblε](https://justine.lol/ape.html) blog post and [cosmopolitan
libc](https://justine.lol/cosmopolitan/index.html) website. We also have libc](https://justine.lol/cosmopolitan/index.html) website. We also have
[API [API documentation](https://justine.lol/cosmopolitan/documentation.html).
documentation](https://justine.lol/cosmopolitan/documentation.html).
## Getting Started ## Getting Started
You can start by obtaining a release of our `cosmocc` compiler from It's recommended that Cosmopolitan be installed to `/opt/cosmo` and
<https://cosmo.zip/pub/cosmocc/>. `/opt/cosmos` on your computer. The first has the monorepo. The second
contains your non-monorepo artifacts.
```sh ```sh
mkdir -p cosmocc sudo mkdir -p /opt
cd cosmocc sudo chmod 1777 /opt
wget https://cosmo.zip/pub/cosmocc/cosmocc.zip git clone https://github.com/jart/cosmopolitan /opt/cosmo
unzip cosmocc.zip cd /opt/cosmo
make -j8 toolchain
ape/apeinstall.sh # optional
mkdir -p /opt/cosmos/bin
export PATH="/opt/cosmos/bin:$PATH"
echo 'PATH="/opt/cosmos/bin:$PATH"' >>~/.profile
sudo ln -sf /opt/cosmo/tool/scripts/cosmocc /opt/cosmos/bin/cosmocc
sudo ln -sf /opt/cosmo/tool/scripts/cosmoc++ /opt/cosmos/bin/cosmoc++
``` ```
Here's an example program we can write: You've now successfully installed your very own cosmos. Now let's build
an example program, which demonstrates the crash reporting feature:
```c ```c
// hello.c // hello.c
#include <stdio.h> #include <stdio.h>
#include <cosmo.h>
int main() { int main() {
ShowCrashReports();
printf("hello world\n"); printf("hello world\n");
__builtin_trap();
} }
``` ```
It can be compiled as follows: To compile the program, you can run the `cosmocc` command. It's
important to give it an output path that ends with `.com` so the output
format will be Actually Portable Executable. When this happens, a
concomitant debug binary is created automatically too.
```sh ```sh
cosmocc -o hello hello.c cosmocc -o hello.com hello.c
./hello ./hello.com
./hello.com.dbg
```
You can use the `cosmocc` toolchain to build conventional open source
projects which use autotools. This strategy normally works:
```sh
export CC=cosmocc
export CXX=cosmoc++
./configure --prefix=/opt/cosmos
make -j
make install
``` ```
The Cosmopolitan Libc runtime links some heavyweight troubleshooting The Cosmopolitan Libc runtime links some heavyweight troubleshooting
@ -53,115 +79,275 @@ features by default, which are very useful for developers and admins.
Here's how you can log system calls: Here's how you can log system calls:
```sh ```sh
./hello --strace ./hello.com --strace
``` ```
Here's how you can get a much more verbose log of function calls: Here's how you can get a much more verbose log of function calls:
```sh ```sh
./hello --ftrace ./hello.com --ftrace
``` ```
You can use the Cosmopolitan's toolchain to build conventional open If you want to cut out the bloat and instead make your executables as
source projects which use autotools. This strategy normally works: tiny as possible, then the monorepo supports numerous build modes. You
can select one of the predefined ones by looking at
[build/config.mk](build/config.mk). One of the most popular modes is
`MODE=tiny`. It can be used with the `cosmocc` toolchain as follows:
```sh ```sh
export CC=x86_64-unknown-cosmo-cc cd /opt/cosmo
export CXX=x86_64-unknown-cosmo-c++ make -j8 MODE=tiny toolchain
./configure --prefix=/opt/cosmos/x86_64
make -j
make install
``` ```
## Cosmopolitan Source Builds Now that we have our toolchain, let's write a program that links less
surface area than the program above. The executable that this program
produces will run on platforms like Linux, Windows, MacOS, etc., even
though it's directly using POSIX APIs, which Cosmopolitan polyfills.
Cosmopolitan can be compiled from source on any of our supported ```c
platforms. The Makefile will download cosmocc automatically. // hello2.c
#include <unistd.h>
int main() {
write(1, "hello world\n", 12);
}
```
It's recommended that you install a systemwide APE Loader. This command Now let's compile our tiny actually portable executable, which should be
requires `sudo` access to copy the `ape` command to a system folder and on the order of 20kb in size.
register with binfmt_misc on Linux, for even more performance.
```sh
export MODE=tiny
cosmocc -Os -o hello2.com hello2.c
./hello2.com
```
Let's say you only care about Linux and would rather have simpler tinier
binaries, similar to what Musl Libc would produce. In that case, try
using the `MODE=tinylinux` build mode, which can produce binaries more
on the order of 4kb.
```sh
export MODE=tinylinux
(cd /opt/cosmo; make -j8 toolchain)
cosmocc -Os -o hello2.com hello2.c
./hello2.com # <-- actually an ELF executable
```
## ARM
Cosmo supports cross-compiling binaries for machines with ARM
microprocessors. There are two options available for doing this.
The first option is to embed the [blink virtual
machine](https://github.com/jart/blink) by adding the following to the
top of your main.c file:
```c
__static_yoink("blink_linux_aarch64"); // for raspberry pi
__static_yoink("blink_xnu_aarch64"); // is apple silicon
```
The benefit is you'll have single file executables that'll run on both
x86_64 and arm64 platforms. The tradeoff is Blink's JIT is slower than
running natively, but tends to go fast enough, unless you're doing
scientific computing (e.g. running LLMs with
`o//third_party/ggml/llama.com`).
Therefore, the second option is to cross compile aarch64 executables,
by using build modes like the following:
```sh
make -j8 m=aarch64 o/aarch64/third_party/ggml/llama.com
make -j8 m=aarch64-tiny o/aarch64-tiny/third_party/ggml/llama.com
```
That'll produce ELF executables that run natively on two operating
systems: Linux Arm64 (e.g. Raspberry Pi) and MacOS Arm64 (i.e. Apple
Silicon), thus giving you full performance. The catch is you have to
compile these executables on an x86_64-linux machine. The second catch
is that MacOS needs a little bit of help understanding the ELF format.
To solve that, we provide a tiny APE loader you can use on M1 machines.
```sh
scp ape/ape-m1.c macintosh:
scp o/aarch64/third_party/ggml/llama.com macintosh:
ssh macintosh
xcode-install
cc -o ape ape-m1.c
sudo cp ape /usr/local/bin/ape
```
You can run your ELF AARCH64 executable on Apple Silicon as follows:
```sh
ape ./llama.com
```
## Source Builds
Cosmopolitan can be compiled from source on any Linux distro. First, you
need to download or clone the repository.
```sh
wget https://justine.lol/cosmopolitan/cosmopolitan.tar.gz
tar xf cosmopolitan.tar.gz # see releases page
cd cosmopolitan
```
This will build the entire repository and run all the tests:
```sh
build/bootstrap/make.com
o//examples/hello.com
find o -name \*.com | xargs ls -rShal | less
```
If you get an error running make.com then it's probably because you have
WINE installed to `binfmt_misc`. You can fix that by installing the the
APE loader as an interpreter. It'll improve build performance too!
```sh ```sh
ape/apeinstall.sh ape/apeinstall.sh
``` ```
You can now build the mono repo with any modern version of GNU Make. To
bootstrap your build, you can install Cosmopolitan Make from this site:
https://cosmo.zip/pub/cosmos/bin/make
E.g.:
```sh
curl -LO https://cosmo.zip/pub/cosmos/bin/make
./make -j8
o//examples/hello
```
After you've built the repo once, you can also use the make from your
cosmocc at `.cosmocc/current/bin/make`. You might even prefer to alias
make to `$COSMO/.cosmocc/current/bin/make`.
Since the Cosmopolitan repository is very large, you might only want to Since the Cosmopolitan repository is very large, you might only want to
build one particular thing. Here's an example of a target that can be build a particular thing. Cosmopolitan's build config does a good job at
compiled relatively quickly, which is a simple POSIX test that only having minimal deterministic builds. For example, if you wanted to build
depends on core LIBC packages. only hello.com then you could do that as follows:
```sh ```sh
rm -rf o//libc o//test build/bootstrap/make.com o//examples/hello.com
.cosmocc/current/bin/make o//test/posix/signal_test
o//test/posix/signal_test
``` ```
Sometimes it's desirable to build a subset of targets, without having to Sometimes it's desirable to build a subset of targets, without having to
list out each individual one. For example if you wanted to build and run list out each individual one. You can do that by asking make to build a
all the unit tests in the `TEST_POSIX` package, you could say: directory name. For example, if you wanted to build only the targets and
subtargets of the chibicc package including its tests, you would say:
```sh ```sh
.cosmocc/current/bin/make o//test/posix build/bootstrap/make.com o//third_party/chibicc
o//third_party/chibicc/chibicc.com --help
``` ```
Cosmopolitan provides a variety of build modes. For example, if you want Cosmopolitan provides a variety of build modes. For example, if you want
really tiny binaries (as small as 12kb in size) then you'd say: really tiny binaries (as small as 12kb in size) then you'd say:
```sh ```sh
.cosmocc/current/bin/make m=tiny build/bootstrap/make.com m=tiny
``` ```
You can furthermore cut out the bloat of other operating systems, and Here's some other build modes you can try:
have Cosmopolitan become much more similar to Musl Libc.
```sh ```sh
.cosmocc/current/bin/make m=tinylinux build/bootstrap/make.com m=dbg # asan + ubsan + debug
build/bootstrap/make.com m=asan # production memory safety
build/bootstrap/make.com m=opt # -march=native optimizations
build/bootstrap/make.com m=rel # traditional release binaries
build/bootstrap/make.com m=optlinux # optimal linux-only performance
build/bootstrap/make.com m=fastbuild # build 28% faster w/o debugging
build/bootstrap/make.com m=tinylinux # tiniest linux-only 4kb binaries
``` ```
For further details, see [//build/config.mk](build/config.mk). For further details, see [//build/config.mk](build/config.mk).
## Debugging ## Cosmopolitan Amalgamation
To print a log of system calls to stderr: Another way to use Cosmopolitan is via our amalgamated release, where
we've combined everything into a single static archive and a single
header file. If you're doing your development work on Linux or BSD then
you need just five files to get started. Here's what you do on Linux:
```sh ```sh
cosmocc -o hello hello.c wget https://justine.lol/cosmopolitan/cosmopolitan-amalgamation-2.2.zip
./hello --strace unzip cosmopolitan-amalgamation-2.2.zip
printf 'main() { printf("hello world\\n"); }\n' >hello.c
gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \
-fno-omit-frame-pointer -pg -mnop-mcount -mno-tls-direct-seg-refs -gdwarf-4 \
-o hello.com.dbg hello.c -fuse-ld=bfd -Wl,-T,ape.lds -Wl,--gc-sections \
-Wl,-z,common-page-size=0x1000 -Wl,-z,max-page-size=0x1000 \
-include cosmopolitan.h crt.o ape-no-modify-self.o cosmopolitan.a
objcopy -S -O binary hello.com.dbg hello.com
``` ```
To print a log of function calls to stderr: You now have a portable program.
```sh ```sh
cosmocc -o hello hello.c ./hello.com
./hello --ftrace bash -c './hello.com' # zsh/fish workaround (we patched them in 2021)
``` ```
Both strace and ftrace use the unbreakable kprintf() facility, which is If `./hello.com` executed on Linux throws an error about not finding an
able to be sent to a file by setting an environment variable. interpreter, it should be fixed by running the following command (although
note that it may not survive a system restart):
```sh ```sh
export KPRINTF_LOG=log sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"
./hello --strace
``` ```
If the same command produces puzzling errors on WSL or WINE when using
Redbean 2.x, they may be fixed by disabling binfmt_misc:
```sh
sudo sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/status'
```
Since we used the `ape-no-modify-self.o` bootloader (rather than
`ape.o`) your executable will not modify itself when it's run. What
it'll instead do, is extract a 4kb program (the [APE loader](https://justine.lol/apeloader/))
to `${TMPDIR:-${HOME:-.}}` that maps your program into memory without
needing to copy it. The APE loader must be in an executable location
(e.g. not stored on a `noexec` mount) for it to run. See below for
alternatives:
It's possible to install the APE loader systemwide as follows.
```sh
# System-Wide APE Install
# for Linux, Darwin, and BSDs
# 1. Copies APE Loader to /usr/bin/ape
# 2. Registers w/ binfmt_misc too if Linux
ape/apeinstall.sh
# System-Wide APE Uninstall
# for Linux, Darwin, and BSDs
ape/apeuninstall.sh
```
It's also possible to convert APE binaries into the system-local format
by using the `--assimilate` flag. Please note that if binfmt_misc is in
play, you'll need to unregister it temporarily before doing this, since
the assimilate feature is part of the shell script header.
```sh
$ file hello.com
hello.com: DOS/MBR boot sector
./hello.com --assimilate
$ file hello.com
hello.com: ELF 64-bit LSB executable
```
Now that you're up and running with Cosmopolitan Libc and APE, here's
some of the most important troubleshooting tools APE offers that you
should know, in case you encounter any issues:
```sh
./hello.com --strace # log system calls to stderr
./hello.com --ftrace # log function calls to stderr
```
Do you love tiny binaries? If so, you may not be happy with Cosmo adding
heavyweight features like tracing to your binaries by default. In that
case, you may want to consider using our build system:
```sh
make m=tiny
```
Which will cause programs such as `hello.com` and `life.com` to shrink
from 60kb in size to about 16kb. There's also a prebuilt amalgamation
online <https://justine.lol/cosmopolitan/cosmopolitan-tiny.zip> hosted
on our download page <https://justine.lol/cosmopolitan/download.html>.
## GDB ## GDB
Here's the recommended `~/.gdbinit` config: Here's the recommended `~/.gdbinit` config:
@ -186,60 +372,46 @@ end
src src
``` ```
You normally run the `.dbg` file under gdb. If you need to debug the You normally run the `.com.dbg` file under gdb. If you need to debug the
`` file itself, then you can load the debug symbols independently as `.com` file itself, then you can load the debug symbols independently as
```sh ```sh
gdb foo -ex 'add-symbol-file foo.dbg 0x401000' gdb foo.com -ex 'add-symbol-file foo.com.dbg 0x401000'
``` ```
## Platform Notes ## Alternative Development Environments
### Shells ### MacOS
If you use zsh and have trouble running APE programs try `sh -c ./prog` If you're developing on MacOS you can install the GNU compiler
or simply upgrade to zsh 5.9+ (since we patched it two years ago). The collection for x86_64-elf via homebrew:
same is the case for Python `subprocess`, old versions of fish, etc.
### Linux
Some Linux systems are configured to launch MZ executables under WINE.
Other distros configure their stock installs so that APE programs will
print "run-detectors: unable to find an interpreter". For example:
```sh ```sh
jart@ubuntu:~$ wget https://cosmo.zip/pub/cosmos/bin/dash brew install x86_64-elf-gcc
jart@ubuntu:~$ chmod +x dash
jart@ubuntu:~$ ./dash
run-detectors: unable to find an interpreter for ./dash
``` ```
You can fix that by registering APE with `binfmt_misc`: Then in the above scripts just replace `gcc` and `objcopy` with
`x86_64-elf-gcc` and `x86_64-elf-objcopy` to compile your APE binary.
### Windows
If you're developing on Windows then you need to download an
x86_64-pc-linux-gnu toolchain beforehand. See the [Compiling on
Windows](https://justine.lol/cosmopolitan/windows-compiling.html)
tutorial. It's needed because the ELF object format is what makes
universal binaries possible.
Cosmopolitan officially only builds on Linux. However, one highly
experimental (and currently broken) thing you could try, is building the
entire cosmo repository from source using the cross9 toolchain.
```sh ```sh
sudo wget -O /usr/bin/ape https://cosmo.zip/pub/cosmos/bin/ape-$(uname -m).elf mkdir -p o/third_party
sudo chmod +x /usr/bin/ape rm -rf o/third_party/gcc
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" wget https://justine.lol/linux-compiler-on-windows/cross9.zip
sudo sh -c "echo ':APE-jart:M::jartsr::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" unzip cross9.zip
``` mv cross9 o/third_party/gcc
build/bootstrap/make.com
You should be good now. APE will not only work, it'll launch executables
400µs faster now too. However if things still didn't work out, it's also
possible to disable `binfmt_misc` as follows:
```sh
sudo sh -c 'echo -1 > /proc/sys/fs/binfmt_misc/cli' # remove Ubuntu's MZ interpreter
sudo sh -c 'echo -1 > /proc/sys/fs/binfmt_misc/status' # remove ALL binfmt_misc entries
```
### WSL
It's normally unsafe to use APE in a WSL environment, because it tries
to run MZ executables as WIN32 binaries within the WSL environment. In
order to make it safe to use Cosmopolitan software on WSL, run this:
```sh
sudo sh -c "echo -1 > /proc/sys/fs/binfmt_misc/WSLInterop"
``` ```
## Discord Chatroom ## Discord Chatroom
@ -249,16 +421,16 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
## Support Vector ## Support Vector
| Platform | Min Version | Circa | | Platform | Min Version | Circa |
| :--- | ---: | ---: | | :--- | ---: | ---: |
| AMD | K8 | 2003 | | AMD | K8 Venus | 2005 |
| Intel | Core | 2006 | | Intel | Core | 2006 |
| Linux | 2.6.18 | 2007 | | Linux | 2.6.18 | 2007 |
| Windows | 8 [1] | 2012 | | Windows | 8 [1] | 2012 |
| Darwin (macOS) | 23.1.0+ | 2023 | | Mac OS X | 15.6 | 2018 |
| OpenBSD | 7.3 or earlier | 2023 | | OpenBSD | 7 | 2021 |
| FreeBSD | 13 | 2020 | | FreeBSD | 13 | 2020 |
| NetBSD | 9.2 | 2021 | | NetBSD | 9.2 | 2021 |
[1] See our [vista branch](https://github.com/jart/cosmopolitan/tree/vista) [1] See our [vista branch](https://github.com/jart/cosmopolitan/tree/vista)
for a community supported version of Cosmopolitan that works on Windows for a community supported version of Cosmopolitan that works on Windows

View file

@ -1,287 +0,0 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
#
# OVERVIEW
#
# αcτµαlly pδrταblε εxεcµταblε
#
# DESCRIPTION
#
# This file defines the libraries, runtimes, and build rules needed to
# create executables from your Linux workstation that'll run anywhere.
# Loading this package will make certain systemic modifications to the
# build like turning off the System V "Red Zone" optimization, because
# αcτµαlly pδrταblε εxεcµταblεs need to be able to run in kernelspace.
PKGS += APE
APE_FILES := $(wildcard ape/*.*)
APE_HDRS = $(filter %.h,$(APE_FILES))
APE_INCS = $(filter %.inc,$(APE_FILES))
ifeq ($(ARCH), aarch64)
APE = o/$(MODE)/ape/aarch64.lds
APELINK = \
$(COMPILE) \
-ALINK.ape \
$(LINK) \
$(LINKARGS) \
$(OUTPUT_OPTION) && \
$(COMPILE) \
-AFIXUP.ape \
-wT$@ \
$(FIXUPOBJ) \
$@
APE_SRCS = ape/ape.S ape/start.S ape/launch.S ape/systemcall.S
APE_OBJS = o/$(MODE)/ape/ape.o
APE_NO_MODIFY_SELF = $(APE)
APE_COPY_SELF = $(APE)
.PHONY: o/$(MODE)/ape
o/$(MODE)/ape: $(APE)
o/$(MODE)/ape/aarch64.lds: \
ape/aarch64.lds \
libc/zip.h \
libc/thread/tls.h \
libc/calls/struct/timespec.h \
libc/macros.h \
libc/str/str.h
APE_LOADER_LDFLAGS = \
-pie \
-static \
-nostdlib \
--no-dynamic-linker \
-z norelro \
-z common-page-size=0x4000 \
-z max-page-size=0x4000
APE_LOADER_FLAGS = \
-DNDEBUG \
-iquote. \
-Wall \
-Wextra \
-fpie \
-Os \
-ffreestanding \
-mgeneral-regs-only \
-fno-asynchronous-unwind-tables \
-fno-stack-protector \
-fno-ident \
-fno-gnu-unique \
-c \
$(OUTPUT_OPTION) \
$<
o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -g $< $@
@$(COMPILE) -AFIXUPOBJ -wT$@ $(FIXUPOBJ) $@
o/$(MODE)/ape/ape.elf.dbg: \
o/$(MODE)/ape/start.o \
o/$(MODE)/ape/loader.o \
o/$(MODE)/ape/launch.o \
o/$(MODE)/ape/systemcall.o
@$(COMPILE) -ALINK.elf $(LD) $(APE_LOADER_LDFLAGS) -o $@ $(patsubst %.lds,-T %.lds,$^)
o/$(MODE)/ape/loader.o: ape/loader.c ape/ape.h
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=33 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/start.o: ape/start.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/ape/launch.o: ape/launch.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/ape/systemcall.o: ape/systemcall.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
.PHONY: o/$(MODE)/ape
o/$(MODE)/ape: o/$(MODE)/ape/ape.elf
else
APE = o/$(MODE)/ape/ape.o \
o/$(MODE)/ape/ape.lds
APELINK = \
$(COMPILE) \
-ALINK.ape \
$(LINK) \
$(LINKARGS) \
$(OUTPUT_OPTION) && \
$(COMPILE) \
-AFIXUP.ape \
-wT$@ \
$(FIXUPOBJ) \
$@
APE_NO_MODIFY_SELF = \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/ape/ape-no-modify-self.o
APE_COPY_SELF = \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/ape/ape-copy-self.o
APE_LOADER_FLAGS = \
-DNDEBUG \
-iquote. \
-Wall \
-Wextra \
-Werror \
-pedantic-errors \
-fpie \
-Os \
-ffreestanding \
-mgeneral-regs-only \
-fno-stack-protector \
-fno-ident \
-fno-gnu-unique \
-c \
$(OUTPUT_OPTION) \
$<
APE_SRCS_C = ape/loader.c
APE_SRCS_S = $(filter %.S,$(APE_FILES))
APE_SRCS = $(APE_SRCS_C) $(APE_SRCS_S)
APE_OBJS = $(APE_SRCS_S:%.S=o/$(MODE)/%.o)
APE_CHECKS = $(APE_HDRS:%=o/%.ok)
o/ape/idata.inc: \
ape/idata.internal.h \
ape/relocations.h
o/$(MODE)/ape/ape-no-modify-self.o: \
ape/ape.S \
ape/ape.h \
ape/macros.internal.h \
ape/relocations.h \
ape/ape.internal.h \
libc/dce.h \
libc/elf/def.h \
libc/thread/tls.h \
libc/macho.h \
libc/macros.h \
libc/nexgen32e/uart.internal.h \
libc/calls/metalfile.internal.h \
libc/nt/pedef.internal.h \
libc/runtime/e820.internal.h \
libc/runtime/mman.internal.h \
libc/runtime/pc.internal.h \
libc/sysv/consts/prot.h \
o/$(MODE)/ape/ape.elf
@$(COMPILE) \
-AOBJECTIFY.S \
$(OBJECTIFY.S) \
$(OUTPUT_OPTION) \
-DAPE_NO_MODIFY_SELF \
-DAPE_LOADER='"o/$(MODE)/ape/ape.elf"' $<
o/$(MODE)/ape/ape-copy-self.o: \
ape/ape.S \
ape/ape.h \
ape/macros.internal.h \
ape/relocations.h \
ape/ape.internal.h \
libc/dce.h \
libc/elf/def.h \
libc/thread/tls.h \
libc/macho.h \
libc/macros.h \
libc/nexgen32e/uart.internal.h \
libc/calls/metalfile.internal.h \
libc/nt/pedef.internal.h \
libc/runtime/e820.internal.h \
libc/runtime/mman.internal.h \
libc/runtime/pc.internal.h \
libc/sysv/consts/prot.h
@$(COMPILE) \
-AOBJECTIFY.S \
$(OBJECTIFY.S) \
$(OUTPUT_OPTION) \
-DAPE_NO_MODIFY_SELF $<
o/$(MODE)/ape/loader.o: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=121 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-gcc.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=121 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-clang.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=121 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-xnu.o: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=8 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-xnu-gcc.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=8 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-xnu-clang.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=8 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg
@$(COMPILE) -AOBJBINCOPY -w $(OBJBINCOPY) -f -o $@ $<
o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.elf.dbg
@$(COMPILE) -AOBJBINCOPY -w $(OBJBINCOPY) -fm -o $@ $<
APE_LOADER_LDFLAGS = \
-static \
-nostdlib \
--no-dynamic-linker \
-z separate-code \
-z common-page-size=0x1000 \
-z max-page-size=0x10000
o/$(MODE)/ape/ape.elf.dbg: \
o/$(MODE)/ape/loader-macho.o \
o/$(MODE)/ape/start.o \
o/$(MODE)/ape/loader.o \
o/$(MODE)/ape/launch.o \
o/$(MODE)/ape/systemcall.o \
ape/loader.lds
@$(COMPILE) -ALINK.elf $(LD) $(APE_LOADER_LDFLAGS) -o $@ $(patsubst %.lds,-T %.lds,$^)
.PHONY: o/$(MODE)/ape
o/$(MODE)/ape: $(APE_CHECKS) \
o/$(MODE)/ape/ape.o \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/ape/ape.elf \
o/$(MODE)/ape/ape.macho \
endif
# these assembly files are safe to build on aarch64
o/$(MODE)/ape/ape.o: \
ape/ape.S \
ape/ape.h \
libc/dce.h \
libc/elf/def.h \
ape/relocations.h \
libc/thread/tls.h \
ape/ape.internal.h \
ape/macros.internal.h \
libc/macho.h \
libc/macros.h \
libc/sysv/consts/prot.h \
libc/nt/pedef.internal.h \
libc/runtime/pc.internal.h \
libc/runtime/e820.internal.h \
libc/runtime/mman.internal.h \
libc/nexgen32e/uart.internal.h \
libc/calls/metalfile.internal.h
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/ape/ape.lds: \
ape/ape.lds \
ape/macros.internal.h \
ape/relocations.h \
ape/ape.internal.h \
libc/thread/tls.h \
libc/calls/struct/timespec.h \
libc/thread/thread.h \
libc/dce.h \
libc/elf/def.h \
libc/elf/pf2prot.internal.h \
libc/macros.h \
libc/nt/pedef.internal.h \
libc/str/str.h \
libc/zip.h

View file

@ -1,7 +1,6 @@
/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│ /*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│
vi: set et sts=2 sw=2 fenc=utf-8 :vi vi: set et sts=2 tw=2 fenc=utf-8 :vi│
╚─────────────────────────────────────────────────────────────────────────────*/ ╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/sysv/consts/prot.h"
#include "libc/thread/tls.h" #include "libc/thread/tls.h"
ENTRY(_start) ENTRY(_start)
@ -12,7 +11,7 @@ OUTPUT_FORMAT("elf64-littleaarch64",
SECTIONS { SECTIONS {
. = SEGMENT_START("text-segment", 0x000800000000); . = SEGMENT_START("text-segment", IMAGE_BASE_VIRTUAL);
__executable_start = .; __executable_start = .;
. += SIZEOF_HEADERS; . += SIZEOF_HEADERS;
@ -90,12 +89,10 @@ SECTIONS {
*(.ubsan.data) *(.ubsan.data)
} }
.notice : { .comment : {
__notices = .; __comment_start = .;
KEEP(*(.notice)) KEEP(*(.comment))
BYTE(0); BYTE(0);
BYTE(10);
BYTE(10);
} }
.eh_frame_hdr : { .eh_frame_hdr : {
@ -103,8 +100,10 @@ SECTIONS {
*(.eh_frame_entry .eh_frame_entry.*) *(.eh_frame_entry .eh_frame_entry.*)
} }
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0; .eh_frame : ONLY_IF_RO {
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0; KEEP(*(.eh_frame))
*(.eh_frame.*)
}
.gcc_except_table : ONLY_IF_RO { .gcc_except_table : ONLY_IF_RO {
*(.gcc_except_table .gcc_except_table.*) *(.gcc_except_table .gcc_except_table.*)
@ -125,11 +124,9 @@ SECTIONS {
. += CONSTANT(MAXPAGESIZE); . += CONSTANT(MAXPAGESIZE);
. = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE)); . = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE));
.eh_frame : { .eh_frame : ONLY_IF_RW {
__eh_frame_start = .;
KEEP(*(.eh_frame)) KEEP(*(.eh_frame))
*(.eh_frame.*) *(.eh_frame.*)
__eh_frame_end = .;
} }
.gnu_extab : ONLY_IF_RW { .gnu_extab : ONLY_IF_RW {
@ -160,11 +157,8 @@ SECTIONS {
.init_array : { .init_array : {
__init_array_start = .; __init_array_start = .;
KEEP(*(.preinit_array)) KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) KEEP(*(.init_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.init_array))
KEEP(*(.ctors))
__init_array_end = .; __init_array_end = .;
} }
@ -259,9 +253,6 @@ SECTIONS {
.debug_ranges 0 : { *(.debug_ranges) } .debug_ranges 0 : { *(.debug_ranges) }
.debug_macro 0 : { *(.debug_macro) } .debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) } .debug_addr 0 : { *(.debug_addr) }
.debug_names 0 : { *(.debug_names) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.ARM.attributes 0 : { KEEP(*(.ARM.attributes)) KEEP(*(.gnu.attributes)) } .ARM.attributes 0 : { KEEP(*(.ARM.attributes)) KEEP(*(.gnu.attributes)) }
.note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) } .note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) }
@ -282,20 +273,18 @@ SECTIONS {
*(.gnu.lto_*) *(.gnu.lto_*)
*(.eh_frame) *(.eh_frame)
*(.idata.*) *(.idata.*)
*(.yoink)
*(.head) *(.head)
} }
} }
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000; ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : APE_STACKSIZE;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024; ape_stack_memsz2 = ape_stack_memsz * 2;
ape_stack_prot = PROT_READ | PROT_WRITE;
_tls_size = _tbss_end - _tdata_start; _tls_size = _tbss_end - _tdata_start;
_tdata_size = _tdata_end - _tdata_start; _tdata_size = _tdata_end - _tdata_start;
_tbss_size = _tbss_end - _tbss_start; _tbss_size = _tbss_end - _tbss_start;
_tbss_offset = _tbss_start - _tdata_start; _tbss_offset = _tbss_start - _tdata_start;
_tls_content = (_tdata_end - _tdata_start) + (_tbss_end - _tbss_start); _tls_content = (_tdata_end - _tdata_start) + (_tbss_end - _tbss_start);
_tdata_align = ALIGNOF(.tdata); _tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss));
_tbss_align = ALIGNOF(.tbss);
_tls_align = MAX(TLS_ALIGNMENT, MAX(ALIGNOF(.tdata), ALIGNOF(.tbss))); ASSERT(ALIGNOF(.tdata) <= TLS_ALIGNMENT && ALIGNOF(.tbss) <= TLS_ALIGNMENT,
"_Thread_local _Alignof can't exceed TLS_ALIGNMENT");

File diff suppressed because it is too large Load diff

184
ape/ape.S
View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 nofixeol :vi vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2020 Justine Alexandra Roberts Tunney
@ -33,17 +33,17 @@
αcτµαlly pδrταblε εxεcµταblε § program header αcτµαlly pδrταblε εxεcµταblε § program header
*/ */
#include "ape/macros.internal.h" #include "ape/macros.internal.h"
#include "ape/notice.inc"
#include "ape/relocations.h" #include "ape/relocations.h"
#include "libc/calls/metalfile.internal.h" #include "libc/calls/metalfile.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/elf/def.h" #include "libc/elf/def.h"
#include "libc/macho.h" #include "libc/macho.internal.h"
#include "libc/nexgen32e/uart.internal.h" #include "libc/nexgen32e/uart.internal.h"
#include "libc/nt/pedef.internal.h" #include "libc/nt/pedef.internal.h"
#include "libc/runtime/pc.internal.h" #include "libc/runtime/pc.internal.h"
#include "ape/ape.internal.h" #include "ape/ape.internal.h"
#include "libc/thread/tls.h" #include "libc/thread/tls.h"
#include "ape/ape.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#define USE_SYMBOL_HACK 1 #define USE_SYMBOL_HACK 1
@ -196,7 +196,7 @@ ape_mz:
.quad ape_elf_entry // 18: e_entry .quad ape_elf_entry // 18: e_entry
.quad ape_elf_phoff // 20: e_phoff .quad ape_elf_phoff // 20: e_phoff
.quad ape_elf_shoff // 28: e_shoff .quad ape_elf_shoff // 28: e_shoff
.long 0x101ca75 // 30: ape e_flags .long 0 // 30: e_flags
.short 64 // 34: e_ehsize .short 64 // 34: e_ehsize
.short 56 // 36: e_phentsize .short 56 // 36: e_phentsize
.short ape_elf_phnum // 38: e_phnum .short ape_elf_phnum // 38: e_phnum
@ -591,7 +591,7 @@ ape_disk:
#ifdef APE_IS_SHELL_SCRIPT #ifdef APE_IS_SHELL_SCRIPT
apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
.ascii "m=$(uname -m 2>/dev/null) || m=x86_64\n" .ascii "m=\"$(uname -m)\"\n"
.ascii "if [ \"$m\" = x86_64 ] || [ \"$m\" = amd64 ]; then\n" .ascii "if [ \"$m\" = x86_64 ] || [ \"$m\" = amd64 ]; then\n"
// Until all operating systems can be updated to support APE, // Until all operating systems can be updated to support APE,
@ -610,9 +610,7 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
// extract the loader into a temp folder, and use it to // extract the loader into a temp folder, and use it to
// load the APE without modifying it. // load the APE without modifying it.
.ascii "[ x\"$1\" != x--assimilate ] && {\n" .ascii "[ x\"$1\" != x--assimilate ] && {\n"
.ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape-" .ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape-1.5\"\n"
.ascii APE_VERSION_STR
.ascii "\"\n"
.ascii "[ -x \"$t\" ] || {\n" .ascii "[ -x \"$t\" ] || {\n"
.ascii "mkdir -p \"${t%/*}\" &&\n" .ascii "mkdir -p \"${t%/*}\" &&\n"
.ascii "dd if=\"$o\" of=\"$t.$$\" skip=" .ascii "dd if=\"$o\" of=\"$t.$$\" skip="
@ -669,7 +667,7 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
.shstub ape_elf_entry,8 // 18: e_entry .shstub ape_elf_entry,8 // 18: e_entry
.shstub ape_elf_phoff,8 // 20: e_phoff .shstub ape_elf_phoff,8 // 20: e_phoff
.shstub ape_elf_shoff,8 // 28: e_shoff .shstub ape_elf_shoff,8 // 28: e_shoff
.ascii "\\165\\312\\1\\1" // 30: ape e_flags .ascii "\\0\\0\\0\\0" // 30: e_flags
.ascii "\\100\\0" // 34: e_ehsize .ascii "\\100\\0" // 34: e_ehsize
.ascii "\\070\\0" // 36: e_phentsize .ascii "\\070\\0" // 36: e_phentsize
.shstub ape_elf_phnum,2 // 38: e_phnum .shstub ape_elf_phnum,2 // 38: e_phnum
@ -699,10 +697,37 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
#endif /* APE_NO_MODIFY_SELF */ #endif /* APE_NO_MODIFY_SELF */
.ascii "exit $?\n" .ascii "exit $?\n"
.ascii "fi\n" // x86_64 .ascii "fi\n" // x86_64
.ascii "echo error: this ape binary only supports x86_64 >&2\n" // ...
.ascii "exit 1\n" // decentralized section (.apesh)
// ...
.ascii "PHDRS='' <<'@'\n"
.endobj apesh .endobj apesh
// elf program headers get inserted here
// because they need to be in the first 4096 bytes
.section .emushprologue,"a",@progbits
emush: .ascii "\n@\n#'\"\n"
.ascii "s=\"$(uname -s)\"\n"
// our script is running on a non-x86_64 architecture
// 1. `dd` out the appropriate blink vm blob
// 2. gunzip the blink virtual machine executable
// 3. relaunch this program inside the blink vm
.ascii "o=\"$(command -v \"$0\")\"\n"
.ascii "e=\"${TMPDIR:-${HOME:-.}}/.ape-blink-1.0.0\"\n"
.previous
// ...
// decentralized section (.emush)
// - __static_yoink("blink_linux_aarch64"); // for raspberry pi
// - __static_yoink("blink_xnu_aarch64"); // is apple silicon
// ...
.section .emushepilogue,"a",@progbits
.ascii "echo \"$0: this ape binary lacks $m support\" >&2\n"
.rept 16
.ascii "exit 127\n"
.endr
.ascii "exit 1\n"
.previous
#ifdef APE_LOADER #ifdef APE_LOADER
.section .ape.loader,"a",@progbits .section .ape.loader,"a",@progbits
.balign 64 .balign 64
@ -720,8 +745,6 @@ ape_loader_end:
#if SupportsSystemv() || SupportsMetal() #if SupportsSystemv() || SupportsMetal()
.section .elf.phdrs,"a",@progbits .section .elf.phdrs,"a",@progbits
.globl ape_phdrs
ape_phdrs:
.long PT_LOAD .long PT_LOAD
.long PF_R|PF_X .long PF_R|PF_X
.stub ape_cod_offset,quad .stub ape_cod_offset,quad
@ -749,19 +772,30 @@ ape_phdrs:
.stub ape_ram_memsz,quad .stub ape_ram_memsz,quad
.stub ape_ram_align,quad .stub ape_ram_align,quad
// These values are left empty because some UNIX OSes give p_filesz // APE Stack Configuration
// priority over `ulimit -s` a.k.a. RLIMIT_STACK which is preferred //
// because we use an 8mb stack by default so that decadent software // We actually respect this when allocating a deterministically
// doesn't unexpectedly crash, but putting that here meant NetBSD's // addressed stack on Windows and other operating systems. However
// rusage accounting (which is the best) always reported 6mb of RSS // there's a few caveats:
//
// - If ape_stack_pf has PF_X then OpenBSD probably won't work
// - We don't bother creating a deterministic stack in MODE=tiny
//
// With that said, here's some examples of configuration:
//
// STATIC_SYMBOL("ape_stack_pf", "7"); // RWX
// STATIC_STACK_ADDR(0x6fffffff0000);
// STATIC_STACK_SIZE(65536);
//
// @see ape.lds for defaults
.long PT_GNU_STACK .long PT_GNU_STACK
.stub ape_stack_pf,long .stub ape_stack_pf,long // override w/ PF_X for execstack
.quad 0 .stub ape_stack_offset,quad // ignored
.quad 0 .stub ape_stack_vaddr,quad // is mmap()'d with MAP_FIXED
.quad 0 .stub ape_stack_paddr,quad // ignored
.quad 0 .stub ape_stack_filesz,quad // ignored
.quad 0 .stub ape_stack_memsz2,quad // is mmap(size) argument
.quad 16 .stub ape_stack_align,quad // must be 16+
#if SupportsOpenbsd() || SupportsNetbsd() #if SupportsOpenbsd() || SupportsNetbsd()
.long PT_NOTE .long PT_NOTE
@ -784,7 +818,7 @@ ape.ident:
.long 1 .long 1
1: .asciz "APE" 1: .asciz "APE"
2: .balign 4 2: .balign 4
3: .long APE_VERSION_NOTE 3: .long 105000000
4: .size ape.ident,.-ape.ident 4: .size ape.ident,.-ape.ident
.type ape.ident,@object .type ape.ident,@object
.previous .previous
@ -1010,8 +1044,7 @@ DLLEXE = DLLSTD
// 6:Contains Initialized Data // 6:Contains Initialized Data
// o 5:Contains Code // o 5:Contains Code
// rrrr oororrorrr // rrrr oororrorrr
PETEXT = 0b01100000000000000000000000100000 PETEXT = 0b01100000000000000000000001100000
PERDAT = 0b01000000000000000000000001000000
PEDATA = 0b11000000000000000000000011000000 PEDATA = 0b11000000000000000000000011000000
PEIMPS = 0b11000000000000000000000001000000 PEIMPS = 0b11000000000000000000000001000000
@ -1019,11 +1052,11 @@ PEIMPS = 0b11000000000000000000000001000000
.balign __SIZEOF_POINTER__ .balign __SIZEOF_POINTER__
ape_pe: .ascin "PE",4 ape_pe: .ascin "PE",4
.short kNtImageFileMachineNexgen32e .short kNtImageFileMachineNexgen32e
.short ape_pe_shnum // NumberOfSections .stub ape_pe_shnum,short // NumberOfSections
.long 0x5c64126b // TimeDateStamp .long 0x5c64126b // TimeDateStamp
.long 0 // PointerToSymbolTable .long 0 // PointerToSymbolTable
.long 0 // NumberOfSymbols .long 0 // NumberOfSymbols
.short ape_pe_optsz // SizeOfOptionalHeader .stub ape_pe_optsz,short // SizeOfOptionalHeader
.short PEEXE // Characteristics .short PEEXE // Characteristics
.short kNtPe64bit // Optional Header Magic .short kNtPe64bit // Optional Header Magic
.byte 14 // MajorLinkerVersion .byte 14 // MajorLinkerVersion
@ -1036,7 +1069,7 @@ ape_pe: .ascin "PE",4
.quad ape_pe_base // ImageBase .quad ape_pe_base // ImageBase
.long ape_pe_sectionalignment // SectionAlignment .long ape_pe_sectionalignment // SectionAlignment
.long ape_pe_filealignment // FileAlignment .long ape_pe_filealignment // FileAlignment
.short 10 // MajorOperatingSystemVersion .short v_ntversion // MajorOperatingSystemVersion
.short 0 // MinorOperatingSystemVersion .short 0 // MinorOperatingSystemVersion
.short 0 // MajorImageVersion .short 0 // MajorImageVersion
.short 0 // MinorImageVersion .short 0 // MinorImageVersion
@ -1047,25 +1080,25 @@ ape_pe: .ascin "PE",4
.long ape_pe_sizeofheaders // SizeOfHeaders .long ape_pe_sizeofheaders // SizeOfHeaders
.long 0 // Checksum .long 0 // Checksum
.short v_ntsubsystem // Subsystem: 0=Neutral,2=GUI,3=Console .short v_ntsubsystem // Subsystem: 0=Neutral,2=GUI,3=Console
.short v_ntdllchar // DllCharacteristics .stub v_ntdllchar,short // DllCharacteristics
.quad 0x10000 // StackReserve .quad 0x0000000000100000 // StackReserve
.quad 0x1000 // StackCommit .quad 0x00000000000fc000 // StackCommit
.quad 0 // HeapReserve .quad 0 // HeapReserve
.quad 0 // HeapCommit .quad 0 // HeapCommit
.long 0 // LoaderFlags .long 0 // LoaderFlags
.long 2 // NumberOfDirectoryEntries .long 2 // NumberOfDirectoryEntries
.long 0,0 // ExportsDirectory .long 0,0 // ExportsDirectory
.long ape_idata // ImportsDirectory .stub ape_idata,long // ImportsDirectory
.long ape_idata_idtsize // ImportsDirectorySize .stub ape_idata_idtsize,long // ImportsDirectorySize
.endobj ape_pe,globl .endobj ape_pe,globl
.previous .previous
.section .pe.sections,"a",@progbits .section .pe.sections,"a",@progbits
.ascin ".text",8 // Section Name .ascin ".text",8 // Section Name
.long ape_text_memsz // Virtual Size or Physical Address .stub ape_text_memsz,long // Virtual Size or Physical Address
.long ape_text_rva // Relative Virtual Address .stub ape_text_rva,long // Relative Virtual Address
.long ape_text_filesz // Physical Size .stub ape_text_filesz,long // Physical Size
.long ape_text_offset // Physical Offset .stub ape_text_offset,long // Physical Offset
.long 0 // Relocation Table Offset .long 0 // Relocation Table Offset
.long 0 // Line Number Table Offset .long 0 // Line Number Table Offset
.short 0 // Relocation Count .short 0 // Relocation Count
@ -1073,25 +1106,12 @@ ape_pe: .ascin "PE",4
.long PETEXT // Flags .long PETEXT // Flags
.previous .previous
.section .pe.sections,"a",@progbits
.ascin ".rdata",8 // Section Name
.long ape_rom_memsz // Virtual Size or Physical Address
.long ape_rom_rva // Relative Virtual Address
.long ape_rom_filesz // Physical Size
.long ape_rom_offset // Physical Offset
.long 0 // Relocation Table Offset
.long 0 // Line Number Table Offset
.short 0 // Relocation Count
.short 0 // Line Number Count
.long PERDAT // Flags
.previous
.section .pe.sections,"a",@progbits .section .pe.sections,"a",@progbits
.ascin ".data",8 // Section Name .ascin ".data",8 // Section Name
.long ape_ram_memsz // Virtual Size or Physical Address .stub ape_ram_memsz,long // Virtual Size or Physical Address
.long ape_ram_rva // Relative Virtual Address .stub ape_ram_rva,long // Relative Virtual Address
.long ape_ram_filesz // Physical Size .stub ape_ram_filesz,long // Physical Size
.long ape_ram_offset // Physical Offset .stub ape_ram_offset,long // Physical Offset
.long 0 // Relocation Table Offset .long 0 // Relocation Table Offset
.long 0 // Line Number Table Offset .long 0 // Line Number Table Offset
.short 0 // Relocation Count .short 0 // Relocation Count
@ -1108,7 +1128,6 @@ ape_pe: .ascin "PE",4
.type ape_idata_idt,@object .type ape_idata_idt,@object
.globl ape_idata_idt,ape_idata_idtend .globl ape_idata_idt,ape_idata_idtend
.hidden ape_idata_idt,ape_idata_idtend .hidden ape_idata_idt,ape_idata_idtend
.balign 4
ape_idata_idt: ape_idata_idt:
.previous/* .previous/*
... ...
@ -1117,7 +1136,6 @@ ape_idata_idt:
*/.section .idata.ro.idt.3,"a",@progbits */.section .idata.ro.idt.3,"a",@progbits
.long 0,0,0,0,0 .long 0,0,0,0,0
ape_idata_idtend: ape_idata_idtend:
.byte 0
.previous .previous
.section .piro.data.sort.iat.1,"aw",@progbits .section .piro.data.sort.iat.1,"aw",@progbits
@ -1125,16 +1143,13 @@ ape_idata_idtend:
.type ape_idata_iat,@object .type ape_idata_iat,@object
.globl ape_idata_iat,ape_idata_iatend .globl ape_idata_iat,ape_idata_iatend
.hidden ape_idata_iat,ape_idata_iatend .hidden ape_idata_iat,ape_idata_iatend
.balign 8
ape_idata_iat: ape_idata_iat:
.previous/* .previous/*
... ...
decentralized content decentralized content
... ...
*/.section .piro.data.sort.iat.3,"aw",@progbits */.section .piro.data.sort.iat.3,"aw",@progbits
.quad 0
ape_idata_iatend: ape_idata_iatend:
.byte 0
.previous .previous
#endif /* SupportsWindows() */ #endif /* SupportsWindows() */
@ -1244,6 +1259,8 @@ realmodeloader:
call lhinit call lhinit
call rlinit call rlinit
call sinit4 call sinit4
.optfn _start16
call _start16
call longmodeloader call longmodeloader
.endfn realmodeloader .endfn realmodeloader
@ -1701,7 +1718,7 @@ ape_grub_entry:
αcτµαlly pδrταblε εxεcµταblε § cosmopolitan libc runtime runtime αcτµαlly pδrταblε εxεcµταblε § cosmopolitan libc runtime runtime
*/ */
kernel: movabs $ape_stack_vaddr,%rsp kernel: movabs $ape_stack_vaddr,%rsp
add $ape_stack_memsz,%rsp add $ape_stack_memsz2,%rsp
movl $0,0x7b000 // unmap null 2mb movl $0,0x7b000 // unmap null 2mb
#if USE_SYMBOL_HACK #if USE_SYMBOL_HACK
.byte 0x0f,0x1f,0207 // nop rdi binbase .byte 0x0f,0x1f,0207 // nop rdi binbase
@ -1771,31 +1788,49 @@ kernel: movabs $ape_stack_vaddr,%rsp
.type ape_text_nops,@object .type ape_text_nops,@object
.type __test_end,@object .type __test_end,@object
.section .commentprologue,"a",@progbits
.globl __comment_start
.type __comment_start,@object
.hidden __comment_start
__comment_start:/*
...
decentralized content
...
*/.previous
.section .commentepilogue,"a",@progbits
.byte 0
.previous
.section .ape.pad.head,"a",@progbits .section .ape.pad.head,"a",@progbits
.type ape_pad_head,@object .type ape_pad_head,@object
.hidden ape_pad_head .hidden ape_pad_head
ape_pad_head: ape_pad_head:
.previous
.section .ape.pad.text,"a",@progbits .section .ape.pad.text,"a",@progbits
.type ape_pad_text,@object .type ape_pad_text,@object
.hidden ape_pad_text .hidden ape_pad_text
ape_pad_text: ape_pad_text:
.previous
.section .ape.pad.privileged,"a",@progbits .section .ape.pad.privileged,"a",@progbits
.type ape_pad_privileged,@object .type ape_pad_privileged,@object
.hidden ape_pad_privileged .hidden ape_pad_privileged
ape_pad_privileged: ape_pad_privileged:
.previous
.section .ape.pad.data,"a",@progbits .section .ape.pad.data,"a",@progbits
.type ape_pad_data,@object .type ape_pad_data,@object
.hidden ape_pad_data .hidden ape_pad_data
ape_pad_data: ape_pad_data:
.previous
#if SupportsWindows() #if SupportsWindows()
.section .idata.ro,"a",@progbits .section .idata.ro,"a",@progbits
.type ape_idata_ro,@object .type ape_idata_ro,@object
.hidden ape_idata_ro .hidden ape_idata_ro
ape_idata_ro: ape_idata_ro:
.previous
#endif /* SupportsWindows() */ #endif /* SupportsWindows() */
.section .dataprologue,"aw",@progbits .section .dataprologue,"aw",@progbits
@ -1803,45 +1838,32 @@ ape_idata_ro:
.globl __data_start .globl __data_start
.hidden __data_start .hidden __data_start
__data_start: __data_start:
.previous
.section .dataepilogue,"aw",@progbits .section .dataepilogue,"aw",@progbits
.type __data_end,@object .type __data_end,@object
.globl __data_end .globl __data_end
.hidden __data_end .hidden __data_end
__data_end: __data_end:
.previous
.section .bssprologue,"aw",@nobits .section .bssprologue,"aw",@nobits
.type __bss_start,@object .type __bss_start,@object
.globl __bss_start .globl __bss_start
.hidden __bss_start .hidden __bss_start
__bss_start: __bss_start:
.previous
.section .bssepilogue,"aw",@nobits .section .bssepilogue,"aw",@nobits
.type __bss_end,@object .type __bss_end,@object
.globl __bss_end .globl __bss_end
.hidden __bss_end .hidden __bss_end
__bss_end: __bss_end:
.previous
.section .fstls,"awT",@nobits .section .fstls,"awT",@nobits
.align TLS_ALIGNMENT .align TLS_ALIGNMENT
.previous
.section .notice,"aR",@progbits
.asciz "\n\n\
Cosmopolitan\n\
Copyright 2024 Justine Alexandra Roberts Tunney\n\
\n\
Permission to use, copy, modify, and/or distribute this software for\n\
any purpose with or without fee is hereby granted, provided that the\n\
above copyright notice and this permission notice appear in all copies.\n\
\n\
THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL\n\
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED\n\
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE\n\
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL\n\
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR\n\
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n\
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n\
PERFORMANCE OF THIS SOFTWARE."
.end .end
 

View file

@ -1,13 +0,0 @@
#ifndef COSMOPOLITAN_APE_APE_H_
#define COSMOPOLITAN_APE_APE_H_
#define APE_VERSION_MAJOR 1
#define APE_VERSION_MINOR 10
#define APE_VERSION_STR APE_VERSION_STR_(APE_VERSION_MAJOR, APE_VERSION_MINOR)
#define APE_VERSION_NOTE APE_VERSION_NOTE_(APE_VERSION_MAJOR, APE_VERSION_MINOR)
#define APE_VERSION_STR__(x, y) #x "." #y
#define APE_VERSION_STR_(x, y) APE_VERSION_STR__(x, y)
#define APE_VERSION_NOTE_(x, y) (100000000 * (x) + 1000000 * (y))
#endif /* COSMOPOLITAN_APE_APE_H_ */

View file

@ -1,5 +1,5 @@
/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│ /*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│
vi: set et sts=2 sw=2 fenc=utf-8 :vi vi: set et sts=2 tw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡ ╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │ │ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │ │ │
@ -198,10 +198,6 @@
#define SKEW 0 #define SKEW 0
#endif #endif
#ifndef IMAGE_BASE_VIRTUAL
#define IMAGE_BASE_VIRTUAL 0x400000
#endif
#if IMAGE_BASE_VIRTUAL > 0xffffffff #if IMAGE_BASE_VIRTUAL > 0xffffffff
#error "please use 32-bit addresses for image data" #error "please use 32-bit addresses for image data"
#endif #endif
@ -229,16 +225,21 @@ SECTIONS {
/* Real Mode */ /* Real Mode */
KEEP(*(.head)) KEEP(*(.head))
KEEP(*(.apesh))
KEEP(*(.text.head)) KEEP(*(.text.head))
/* Executable & Linkable Format */ /* Executable & Linkable Format */
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0); . = ALIGN(__SIZEOF_POINTER__);
ape_phdrs = .; ape_phdrs = .;
KEEP(*(.elf.phdrs)) KEEP(*(.elf.phdrs))
ape_phdrs_end = .; ape_phdrs_end = .;
KEEP(*(.emushprologue))
KEEP(*(.emush))
KEEP(*(.emushepilogue))
/* OpenBSD */ /* OpenBSD */
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0); . = ALIGN(__SIZEOF_POINTER__);
ape_note = .; ape_note = .;
KEEP(*(.note.openbsd.ident)) KEEP(*(.note.openbsd.ident))
KEEP(*(.note.netbsd.ident)) KEEP(*(.note.netbsd.ident))
@ -252,15 +253,15 @@ SECTIONS {
/* Mach-O */ /* Mach-O */
KEEP(*(.macho)) KEEP(*(.macho))
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0); . = ALIGN(__SIZEOF_POINTER__);
ape_macho_end = .; ape_macho_end = .;
/* APE loader */ /* APE loader */
KEEP(*(.ape.loader)) KEEP(*(.ape.loader))
. = ALIGN(. != 0 ? CODE_GRANULE : 0); . = ALIGN(CODE_GRANULE);
KEEP(*(.ape.pad.head)) KEEP(*(.ape.pad.head))
. = ALIGN(. != 0 ? (SupportsWindows() || SupportsMetal() ? CONSTANT(COMMONPAGESIZE) : 16) : 0); . = ALIGN(SupportsWindows() || SupportsMetal() ? CONSTANT(COMMONPAGESIZE) : 16);
_ehead = .; _ehead = .;
} :Head } :Head
@ -282,9 +283,12 @@ SECTIONS {
KEEP(*(SORT_BY_NAME(.init.*))) KEEP(*(SORT_BY_NAME(.init.*)))
KEEP(*(.init)) KEEP(*(.init))
KEEP(*(.initepilogue)) KEEP(*(.initepilogue))
KEEP(*(.pltprologue))
*(.plt) *(.plt)
KEEP(*(.pltepilogue))
KEEP(*(.pltgotprologue))
*(.plt.got) *(.plt.got)
*(.iplt) KEEP(*(.pltgotepilogue))
*(.text.startup .text.startup.*) *(.text.startup .text.startup.*)
*(.text.exit .text.exit.*) *(.text.exit .text.exit.*)
*(.text.unlikely .text.*_unlikely .text.unlikely.*) *(.text.unlikely .text.*_unlikely .text.unlikely.*)
@ -293,6 +297,7 @@ SECTIONS {
KEEP(*(.textwindowsprologue)) KEEP(*(.textwindowsprologue))
*(.text.windows) *(.text.windows)
KEEP(*(.textwindowsepilogue)) KEEP(*(.textwindowsepilogue))
KEEP(*(.blink))
*(SORT_BY_ALIGNMENT(.text.modernity)) *(SORT_BY_ALIGNMENT(.text.modernity))
*(SORT_BY_ALIGNMENT(.text.modernity.*)) *(SORT_BY_ALIGNMENT(.text.modernity.*))
*(SORT_BY_ALIGNMENT(.text.hot)) *(SORT_BY_ALIGNMENT(.text.hot))
@ -307,20 +312,20 @@ SECTIONS {
/* Privileged code invulnerable to magic */ /* Privileged code invulnerable to magic */
KEEP(*(.ape.pad.privileged)); KEEP(*(.ape.pad.privileged));
. = ALIGN(__privileged_end > __privileged_start ? CONSTANT(COMMONPAGESIZE) : 0); . = ALIGN(__privileged_end > __privileged_start ? CONSTANT(COMMONPAGESIZE) : 1);
/*END: morphable code */ /*END: morphable code */
__privileged_start = .; __privileged_start = .;
*(.privileged .privileged.*) *(.privileged)
__privileged_end = .; __privileged_end = .;
KEEP(*(.ape.pad.text)) KEEP(*(.ape.pad.text))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0); . = ALIGN(CONSTANT(COMMONPAGESIZE));
/*END: Read Only Data (only needed for initialization) */ /*END: Read Only Data (only needed for initialization) */
} :Cod } :Cod
/*BEGIN: Read Only Data */ /*BEGIN: Read Only Data */
.rodata ALIGN(CONSTANT(COMMONPAGESIZE)) : { .rodata . : {
KEEP(*(.rodata.pytab.0)); KEEP(*(.rodata.pytab.0));
KEEP(*(.rodata.pytab.1)); KEEP(*(.rodata.pytab.1));
KEEP(*(.rodata.pytab.2)); KEEP(*(.rodata.pytab.2));
@ -329,16 +334,12 @@ SECTIONS {
*(.ubsan.types) *(.ubsan.types)
*(.ubsan.data) *(.ubsan.data)
__eh_frame_hdr_start_actual = .;
*(.eh_frame_hdr)
__eh_frame_hdr_end_actual = .;
/* Legal Notices */ /* Legal Notices */
__notices = .; #if !defined(IM_FEELING_NAUGHTY) || defined(EMBED_NOTICES)
KEEP(*(.notice)) KEEP(*(.commentprologue))
BYTE(0); KEEP(*(.comment))
BYTE(10); KEEP(*(.commentepilogue))
BYTE(10); #endif
/*BEGIN: read-only data that's only needed for initialization */ /*BEGIN: read-only data that's only needed for initialization */
@ -348,12 +349,28 @@ SECTIONS {
KEEP(*(SORT_BY_NAME(.idata.ro.*))) KEEP(*(SORT_BY_NAME(.idata.ro.*)))
#endif #endif
. = ALIGN(__SIZEOF_POINTER__);
__init_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)
SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.ctors))
KEEP(*(.init_array))
KEEP(*(.preinit_array))
__init_array_end = .;
. = ALIGN(__SIZEOF_POINTER__);
__fini_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)
SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP(*(.fini_array))
KEEP(*(.dtors))
__fini_array_end = .;
/* Encoded Data Structures w/ Linear Initialization Order */ /* Encoded Data Structures w/ Linear Initialization Order */
KEEP(*(.initroprologue)) KEEP(*(.initroprologue))
KEEP(*(SORT_BY_NAME(.initro.*))) KEEP(*(SORT_BY_NAME(.initro.*)))
KEEP(*(.initroepilogue)) KEEP(*(.initroepilogue))
KEEP(*(SORT_BY_NAME(.sort.rodata.*))) KEEP(*(SORT_BY_NAME(.sort.rodata.*)))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0); /* don't delete this line :o */
/*END: read-only data that's only needed for initialization */ /*END: read-only data that's only needed for initialization */
@ -367,13 +384,14 @@ SECTIONS {
*(SORT_BY_ALIGNMENT(.tdata.*)) *(SORT_BY_ALIGNMENT(.tdata.*))
_tdata_end = .; _tdata_end = .;
KEEP(*(.ape.pad.rodata)) KEEP(*(.ape.pad.rodata))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0); . = ALIGN(CONSTANT(COMMONPAGESIZE));
_etext = .; _etext = .;
PROVIDE(etext = .); PROVIDE(etext = .);
} :Tls :Rom } :Tls :Rom
/*END: Read Only Data */ /*END: Read Only Data */
. = ALIGN(CONSTANT(COMMONPAGESIZE)); . = DATA_SEGMENT_ALIGN(CONSTANT(COMMONPAGESIZE), CONSTANT(COMMONPAGESIZE));
. = DATA_SEGMENT_RELRO_END(0, .);
/* this only tells the linker about the layout of uninitialized */ /* this only tells the linker about the layout of uninitialized */
/* TLS data, and does not advance the linker's location counter */ /* TLS data, and does not advance the linker's location counter */
@ -386,13 +404,6 @@ SECTIONS {
_tbss_end = .; _tbss_end = .;
} :Tls } :Tls
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
} :Ram
.data . : { .data . : {
/*BEGIN: Read/Write Data */ /*BEGIN: Read/Write Data */
#if SupportsWindows() #if SupportsWindows()
@ -401,55 +412,33 @@ SECTIONS {
/*BEGIN: NT FORK COPYING */ /*BEGIN: NT FORK COPYING */
KEEP(*(.dataprologue)) KEEP(*(.dataprologue))
*(.data .data.*) *(.data .data.*)
*(.gnu_extab)
*(.gcc_except_table .gcc_except_table.*)
*(.exception_ranges*)
*(.PyRuntime) /* for python */ *(.PyRuntime) /* for python */
*(.subrs) /* for emacs */ *(.subrs) /* for emacs */
KEEP(*(SORT_BY_NAME(.sort.data.*))) KEEP(*(SORT_BY_NAME(.sort.data.*)))
. += . > 0 ? CODE_GRANULE : 0; . += . > 0 ? CODE_GRANULE : 0;
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0); KEEP(*(.gotprologue))
__got_start = .;
*(.got) *(.got)
__got_end = .; KEEP(*(.gotepilogue))
KEEP(*(.gotpltprologue))
*(.got.plt) *(.got.plt)
KEEP(*(.gotpltepilogue))
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
__init_array_start = .;
KEEP(*(.preinit_array))
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)
SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP(*(.init_array))
KEEP(*(.ctors))
__init_array_end = .;
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
__fini_array_start = .;
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)
SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP(*(.fini_array))
KEEP(*(.dtors))
__fini_array_end = .;
/*BEGIN: Post-Initialization Read-Only */ /*BEGIN: Post-Initialization Read-Only */
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0); . = ALIGN(__SIZEOF_POINTER__);
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*))) KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0); . = ALIGN(__SIZEOF_POINTER__);
KEEP(*(SORT_BY_NAME(.piro.data.sort.*))) KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
KEEP(*(.piro.pad.data)) KEEP(*(.piro.pad.data))
*(.igot.plt)
KEEP(*(.dataepilogue)) KEEP(*(.dataepilogue))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0); . = ALIGN(512);
/*END: NT FORK COPYING */ /*END: NT FORK COPYING */
_edata = .; _edata = .;
PROVIDE(edata = .); PROVIDE(edata = .);
_ezip = .; /* <-- very deprecated */ _ezip = .; /* <-- very deprecated */
} :Ram } :Ram
. = ALIGN(CONSTANT(COMMONPAGESIZE));
/*END: file content that's loaded by o/s */ /*END: file content that's loaded by o/s */
/*END: file content */ /*END: file content */
/*BEGIN: bss memory that's addressable */ /*BEGIN: bss memory that's addressable */
@ -473,14 +462,14 @@ SECTIONS {
KEEP(*(.bssepilogue)) KEEP(*(.bssepilogue))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0); . = ALIGN(CONSTANT(COMMONPAGESIZE));
/*END: NT FORK COPYING */ /*END: NT FORK COPYING */
_end = .;
PROVIDE(end = .);
} :Ram } :Ram
. = ALIGN(CONSTANT(COMMONPAGESIZE)); . = DATA_SEGMENT_END(.);
_end = .;
PROVIDE(end = .);
/*END: nt addressability guarantee */ /*END: nt addressability guarantee */
/*END: bsd addressability guarantee */ /*END: bsd addressability guarantee */
@ -520,9 +509,6 @@ SECTIONS {
.debug_rnglists 0 : { *(.debug_rnglists) } .debug_rnglists 0 : { *(.debug_rnglists) }
.debug_macro 0 : { *(.debug_macro) } .debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) } .debug_addr 0 : { *(.debug_addr) }
.debug_names 0 : { *(.debug_names) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.gnu.attributes 0 : { KEEP(*(.gnu.attributes)) } .gnu.attributes 0 : { KEEP(*(.gnu.attributes)) }
.GCC.command.line 0 : { *(.GCC.command.line) } .GCC.command.line 0 : { *(.GCC.command.line) }
@ -541,9 +527,7 @@ SECTIONS {
*(.piro.data.sort.iat.*) *(.piro.data.sort.iat.*)
#endif #endif
*(__patchable_function_entries) *(__patchable_function_entries)
*(.note.gnu.property)
*(__mcount_loc) *(__mcount_loc)
*(.rela.dyn)
*(.discard) *(.discard)
*(.yoink) *(.yoink)
} }
@ -566,31 +550,29 @@ _tdata_size = _tdata_end - _tdata_start;
_tbss_size = _tbss_end - _tbss_start; _tbss_size = _tbss_end - _tbss_start;
_tbss_offset = _tbss_start - _tdata_start; _tbss_offset = _tbss_start - _tdata_start;
_tls_content = (_tdata_end - _tdata_start) + (_tbss_end - _tbss_start); _tls_content = (_tdata_end - _tdata_start) + (_tbss_end - _tbss_start);
_tdata_align = ALIGNOF(.tdata); _tls_align = 1;
_tbss_align = ALIGNOF(.tbss);
_tls_align = MAX(TLS_ALIGNMENT, MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)));
ape_cod_offset = 0; ape_cod_offset = 0;
ape_cod_vaddr = ADDR(.head); ape_cod_vaddr = ADDR(.head);
ape_cod_paddr = LOADADDR(.head); ape_cod_paddr = LOADADDR(.head);
ape_cod_filesz = ADDR(.rodata) - ADDR(.head); ape_cod_filesz = SIZEOF(.head) + SIZEOF(.text);
ape_cod_memsz = ape_cod_filesz; ape_cod_memsz = ape_cod_filesz;
ape_cod_align = CONSTANT(COMMONPAGESIZE); ape_cod_align = CONSTANT(COMMONPAGESIZE);
ape_cod_rva = RVA(ape_cod_vaddr); ape_cod_rva = RVA(ape_cod_vaddr);
ape_rom_offset = ape_cod_offset + ape_cod_filesz;
ape_rom_vaddr = ADDR(.rodata); ape_rom_vaddr = ADDR(.rodata);
ape_rom_offset = ape_rom_vaddr - __executable_start;
ape_rom_paddr = LOADADDR(.rodata); ape_rom_paddr = LOADADDR(.rodata);
ape_rom_filesz = ADDR(.tbss) - ADDR(.rodata); ape_rom_filesz = SIZEOF(.rodata) + SIZEOF(.tdata);
ape_rom_memsz = ape_rom_filesz; ape_rom_memsz = ape_rom_filesz;
ape_rom_align = CONSTANT(COMMONPAGESIZE); ape_rom_align = CONSTANT(COMMONPAGESIZE);
ape_rom_rva = RVA(ape_rom_vaddr); ape_rom_rva = RVA(ape_rom_vaddr);
ape_ram_vaddr = ADDR(.eh_frame); ape_ram_offset = ape_rom_offset + ape_rom_filesz;
ape_ram_offset = ape_ram_vaddr - __executable_start; ape_ram_vaddr = ADDR(.data);
ape_ram_paddr = LOADADDR(.eh_frame); ape_ram_paddr = LOADADDR(.data);
ape_ram_filesz = ADDR(.bss) - ADDR(.eh_frame); ape_ram_filesz = SIZEOF(.data);
ape_ram_memsz = _end - ADDR(.eh_frame); ape_ram_memsz = SIZEOF(.data) + SIZEOF(.bss);
ape_ram_align = CONSTANT(COMMONPAGESIZE); ape_ram_align = CONSTANT(COMMONPAGESIZE);
ape_ram_rva = RVA(ape_ram_vaddr); ape_ram_rva = RVA(ape_ram_vaddr);
@ -600,23 +582,22 @@ ape_stack_offset = 0;
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000; ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz; ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
ape_stack_filesz = 0; ape_stack_filesz = 0;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 4 * 1024 * 1024; ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : APE_STACKSIZE;
ape_stack_memsz2 = ape_stack_memsz * 2;
ape_stack_align = 16;
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr); ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
ape_note_filesz = ape_note_end - ape_note; ape_note_filesz = ape_note_end - ape_note;
ape_note_memsz = ape_note_filesz; ape_note_memsz = ape_note_filesz;
ape_text_vaddr = ADDR(.text); ape_text_offset = ape_cod_offset + LOADADDR(.text) - ape_cod_paddr;
ape_text_offset = ape_text_vaddr - __executable_start;
ape_text_paddr = LOADADDR(.text); ape_text_paddr = LOADADDR(.text);
ape_text_filesz = ADDR(.rodata) - ADDR(.text); ape_text_vaddr = ADDR(.text);
ape_text_filesz = SIZEOF(.text) + SIZEOF(.rodata) + SIZEOF(.tdata);
ape_text_memsz = ape_text_filesz; ape_text_memsz = ape_text_filesz;
ape_text_align = CONSTANT(COMMONPAGESIZE); ape_text_align = CONSTANT(COMMONPAGESIZE);
ape_text_rva = RVA(ape_text_vaddr); ape_text_rva = RVA(ape_text_vaddr);
__eh_frame_hdr_start = __eh_frame_hdr_end_actual > __eh_frame_hdr_start_actual ? __eh_frame_hdr_start_actual : 0;
__eh_frame_hdr_end = __eh_frame_hdr_end_actual > __eh_frame_hdr_start_actual ? __eh_frame_hdr_end_actual : 0;
/* we roundup here because xnu wants the file load segments page-aligned */ /* we roundup here because xnu wants the file load segments page-aligned */
/* but we don't want to add the nop padding to the ape program, so we'll */ /* but we don't want to add the nop padding to the ape program, so we'll */
/* let ape.S dd read past the end of the file into the wrapping binaries */ /* let ape.S dd read past the end of the file into the wrapping binaries */
@ -626,6 +607,29 @@ SHSTUB2(ape_loader_dd_count,
? ROUNDUP(ape_loader_end - ape_loader, CONSTANT(COMMONPAGESIZE)) / 64 ? ROUNDUP(ape_loader_end - ape_loader, CONSTANT(COMMONPAGESIZE)) / 64
: 0); : 0);
#if defined(APE_IS_SHELL_SCRIPT) && !IsTiny()
#define IDENTITY(X) X
#define APE_DECLARE_FIXED_DECIMAL(F, X) \
X##_quad = DEFINED(X) ? ((F(X) < 1000000000 ? 32 : F(X) / 1000000000 % 10 + 48) << 000 | \
(F(X) < 100000000 ? 32 : F(X) / 100000000 % 10 + 48) << 010 | \
(F(X) < 10000000 ? 32 : F(X) / 10000000 % 10 + 48) << 020 | \
(F(X) < 1000000 ? 32 : F(X) / 1000000 % 10 + 48) << 030 | \
(F(X) < 100000 ? 32 : F(X) / 100000 % 10 + 48) << 040 | \
(F(X) < 10000 ? 32 : F(X) / 10000 % 10 + 48) << 050 | \
(F(X) < 1000 ? 32 : F(X) / 1000 % 10 + 48) << 060 | \
(F(X) < 100 ? 32 : F(X) / 100 % 10 + 48) << 070) : 0; \
X##_short = DEFINED(X) ? ((F(X) < 10 ? 32 : F(X) / 10 % 10 + 48) << 000 | \
(F(X) % 10 + 48) << 010) : 0
APE_DECLARE_FIXED_DECIMAL(RVA, blink_linux_aarch64);
APE_DECLARE_FIXED_DECIMAL(IDENTITY, blink_linux_aarch64_size);
APE_DECLARE_FIXED_DECIMAL(RVA, blink_xnu_aarch64);
APE_DECLARE_FIXED_DECIMAL(IDENTITY, blink_xnu_aarch64_size);
#endif /* APE_IS_SHELL_SCRIPT */
#if SupportsMetal() #if SupportsMetal()
v_ape_realsectors = MIN(0x70000 - IMAGE_BASE_REAL, ROUNDUP(RVA(_ezip), 512)) / 512; v_ape_realsectors = MIN(0x70000 - IMAGE_BASE_REAL, ROUNDUP(RVA(_ezip), 512)) / 512;
v_ape_realbytes = v_ape_realsectors * 512; v_ape_realbytes = v_ape_realsectors * 512;
@ -729,6 +733,7 @@ ape_idataz = LINK_WINDOWS ? RVA(ape_idata_iat) : 0;
ape_idata_iatsize = LINK_WINDOWS ? ape_idata_iatend - ape_idata_iat : 0; ape_idata_iatsize = LINK_WINDOWS ? ape_idata_iatend - ape_idata_iat : 0;
ape_idata = LINK_WINDOWS ? RVA(ape_idata_idt) : 0; ape_idata = LINK_WINDOWS ? RVA(ape_idata_idt) : 0;
ape_idata_idtsize = LINK_WINDOWS ? ape_idata_idtend - ape_idata_idt : 0; ape_idata_idtsize = LINK_WINDOWS ? ape_idata_idtend - ape_idata_idt : 0;
v_ntversion = LINK_WINDOWS ? 6 : 1;
v_ntdllchar = LINK_WINDOWS ? 288 : 0; v_ntdllchar = LINK_WINDOWS ? 288 : 0;
v_ntsubversion = LINK_WINDOWS ? 6 : 5; v_ntsubversion = LINK_WINDOWS ? 6 : 5;
v_ntsubsystem = (LINK_WINDOWS v_ntsubsystem = (LINK_WINDOWS
@ -754,6 +759,12 @@ ASSERT(((DEFINED(__init_rodata_end) ? __init_rodata_end : 0) %
ASSERT((!DEFINED(ape_grub) ? 1 : RVA(ape_grub) < 8192), ASSERT((!DEFINED(ape_grub) ? 1 : RVA(ape_grub) < 8192),
"grub stub needs to be in first 8kb of image"); "grub stub needs to be in first 8kb of image");
ASSERT(DEFINED(_start) || DEFINED(_start16),
"please link a _start() or _start16() entrypoint");
ASSERT(!DEFINED(_start16) || REAL(_end) < 65536,
"ape won't support non-tiny real mode programs");
ASSERT(IS2POW(ape_stack_memsz), ASSERT(IS2POW(ape_stack_memsz),
"ape_stack_memsz must be a two power"); "ape_stack_memsz must be a two power");
@ -763,8 +774,6 @@ ASSERT(ape_stack_vaddr % ape_stack_memsz == 0,
ASSERT(ALIGNOF(.tdata) <= TLS_ALIGNMENT && ALIGNOF(.tbss) <= TLS_ALIGNMENT, ASSERT(ALIGNOF(.tdata) <= TLS_ALIGNMENT && ALIGNOF(.tbss) <= TLS_ALIGNMENT,
"_Thread_local _Alignof can't exceed TLS_ALIGNMENT"); "_Thread_local _Alignof can't exceed TLS_ALIGNMENT");
ASSERT(DEFINED(main), "main() function not defined");
/* Let's not be like Knight Capital. */ /* Let's not be like Knight Capital. */
/* NOCROSSREFS_TO(.test .text) */ /* NOCROSSREFS_TO(.test .text) */

303
ape/ape.mk Normal file
View file

@ -0,0 +1,303 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
#
# OVERVIEW
#
# αcτµαlly pδrταblε εxεcµταblε
#
# DESCRIPTION
#
# This file defines the libraries, runtimes, and build rules needed to
# create executables from your Linux workstation that'll run anywhere.
# Loading this package will make certain systemic modifications to the
# build like turning off the System V "Red Zone" optimization, because
# αcτµαlly pδrταblε εxεcµταblεs need to be able to run in kernelspace.
PKGS += APE
APE_FILES := $(wildcard ape/*.*)
APE_HDRS = $(filter %.h,$(APE_FILES))
APE_INCS = $(filter %.inc,$(APE_FILES))
ifeq ($(ARCH), aarch64)
APE = o/$(MODE)/ape/aarch64.lds
APELINK = \
$(COMPILE) \
-ALINK.ape \
$(LINK) \
$(LINKARGS) \
$(OUTPUT_OPTION) && \
$(COMPILE) \
-AFIXUP.ape \
-wT$@ \
$(FIXUPOBJ) \
$@
APE_SRCS = ape/ape.S ape/start.S ape/launch.S ape/systemcall.S
APE_OBJS = o/$(MODE)/ape/ape.o
APE_NO_MODIFY_SELF = $(APE)
APE_COPY_SELF = $(APE)
.PHONY: o/$(MODE)/ape
o/$(MODE)/ape: $(APE)
o/$(MODE)/ape/aarch64.lds: \
ape/aarch64.lds \
libc/zip.internal.h \
libc/intrin/bits.h \
libc/thread/tls.h \
libc/calls/struct/timespec.h \
libc/macros.internal.h \
libc/str/str.h
APE_LOADER_LDFLAGS = \
-pie \
-static \
-nostdlib \
--no-dynamic-linker \
-z norelro \
-z common-page-size=0x4000 \
-z max-page-size=0x4000
APE_LOADER_FLAGS = \
-DNDEBUG \
-iquote. \
-Wall \
-Wextra \
-fpie \
-Os \
-ffreestanding \
-mgeneral-regs-only \
-fno-asynchronous-unwind-tables \
-fno-stack-protector \
-fno-ident \
-fno-gnu-unique \
-c \
$(OUTPUT_OPTION) \
$<
o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg
$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -g $< $@
o/$(MODE)/ape/ape.elf.dbg: \
o/$(MODE)/ape/start.o \
o/$(MODE)/ape/loader.o \
o/$(MODE)/ape/launch.o \
o/$(MODE)/ape/systemcall.o
@$(COMPILE) -ALINK.elf $(LD) $(APE_LOADER_LDFLAGS) -o $@ $(patsubst %.lds,-T %.lds,$^)
o/$(MODE)/ape/loader.o: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=1 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/start.o: ape/start.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/ape/launch.o: ape/launch.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/ape/systemcall.o: ape/systemcall.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
.PHONY: o/$(MODE)/ape
o/$(MODE)/ape: o/$(MODE)/ape/ape.elf
else
APE = o/$(MODE)/ape/ape.o \
o/$(MODE)/ape/ape.lds
APELINK = \
$(COMPILE) \
-ALINK.ape \
$(LINK) \
$(LINKARGS) \
$(OUTPUT_OPTION) && \
$(COMPILE) \
-AFIXUP.ape \
-wT$@ \
$(FIXUPOBJ) \
$@
APE_NO_MODIFY_SELF = \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/ape/ape-no-modify-self.o
APE_COPY_SELF = \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/ape/ape-copy-self.o
APE_LOADER_FLAGS = \
-DNDEBUG \
-iquote. \
-Wall \
-Wextra \
-fpie \
-Os \
-ffreestanding \
-mgeneral-regs-only \
-fno-stack-protector \
-fno-ident \
-fno-gnu-unique \
-c \
$(OUTPUT_OPTION) \
$<
APE_SRCS_C = ape/loader.c
APE_SRCS_S = $(filter %.S,$(APE_FILES))
APE_SRCS = $(APE_SRCS_C) $(APE_SRCS_S)
APE_OBJS = $(APE_SRCS_S:%.S=o/$(MODE)/%.o)
APE_CHECKS = $(APE_HDRS:%=o/%.ok)
o/$(MODE)/ape/public/ape.lds: CPPFLAGS += -UCOSMO
o/$(MODE)/ape/public/ape.lds: \
ape/public/ape.lds \
ape/ape.lds \
ape/ape.internal.h \
ape/macros.internal.h \
ape/relocations.h \
libc/intrin/bits.h \
libc/thread/tls.h \
libc/calls/struct/timespec.h \
libc/thread/thread.h \
libc/dce.h \
libc/elf/def.h \
libc/elf/pf2prot.internal.h \
libc/macros.internal.h \
libc/nt/pedef.internal.h \
libc/str/str.h \
libc/zip.internal.h
o/ape/idata.inc: \
ape/idata.internal.h \
ape/relocations.h
o/$(MODE)/ape/ape-no-modify-self.o: \
ape/ape.S \
ape/macros.internal.h \
ape/notice.inc \
ape/relocations.h \
ape/ape.internal.h \
libc/dce.h \
libc/elf/def.h \
libc/thread/tls.h \
libc/intrin/asancodes.h \
libc/macho.internal.h \
libc/macros.internal.h \
libc/nexgen32e/uart.internal.h \
libc/calls/metalfile.internal.h \
libc/nexgen32e/vidya.internal.h \
libc/nt/pedef.internal.h \
libc/runtime/e820.internal.h \
libc/runtime/mman.internal.h \
libc/runtime/pc.internal.h \
libc/sysv/consts/prot.h \
ape/blink-linux-aarch64.gz \
ape/blink-xnu-aarch64.gz \
o/$(MODE)/ape/ape.elf
@$(COMPILE) \
-AOBJECTIFY.S \
$(OBJECTIFY.S) \
$(OUTPUT_OPTION) \
-DAPE_NO_MODIFY_SELF \
-DAPE_LOADER='"o/$(MODE)/ape/ape.elf"' $<
o/$(MODE)/ape/ape-copy-self.o: \
ape/ape.S \
ape/macros.internal.h \
ape/notice.inc \
ape/relocations.h \
ape/ape.internal.h \
libc/dce.h \
libc/elf/def.h \
libc/thread/tls.h \
libc/intrin/asancodes.h \
libc/macho.internal.h \
libc/macros.internal.h \
libc/nexgen32e/uart.internal.h \
libc/calls/metalfile.internal.h \
libc/nexgen32e/vidya.internal.h \
libc/nt/pedef.internal.h \
libc/runtime/e820.internal.h \
libc/runtime/mman.internal.h \
libc/runtime/pc.internal.h \
libc/sysv/consts/prot.h \
ape/blink-linux-aarch64.gz \
ape/blink-xnu-aarch64.gz
@$(COMPILE) \
-AOBJECTIFY.S \
$(OBJECTIFY.S) \
$(OUTPUT_OPTION) \
-DAPE_NO_MODIFY_SELF $<
o/$(MODE)/ape/loader.o: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=121 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-gcc.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=121 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-clang.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=121 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-xnu.o: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=8 -g $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-xnu-gcc.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CC) -DSUPPORT_VECTOR=8 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/loader-xnu-clang.asm: ape/loader.c
@$(COMPILE) -AOBJECTIFY.c $(CLANG) -DSUPPORT_VECTOR=8 -S -g0 $(APE_LOADER_FLAGS)
o/$(MODE)/ape/ape.elf: o/$(MODE)/ape/ape.elf.dbg
@$(COMPILE) -AOBJBINCOPY -w build/bootstrap/objbincopy.com -f -o $@ $<
o/$(MODE)/ape/ape.macho: o/$(MODE)/ape/ape.elf.dbg
@$(COMPILE) -AOBJBINCOPY -w build/bootstrap/objbincopy.com -fm -o $@ $<
APE_LOADER_LDFLAGS = \
-static \
-nostdlib \
--no-dynamic-linker \
-z separate-code \
-z common-page-size=0x1000 \
-z max-page-size=0x10000
o/$(MODE)/ape/ape.elf.dbg: \
o/$(MODE)/ape/loader-macho.o \
o/$(MODE)/ape/start.o \
o/$(MODE)/ape/loader.o \
o/$(MODE)/ape/launch.o \
o/$(MODE)/ape/systemcall.o \
ape/loader.lds
@$(COMPILE) -ALINK.elf $(LD) $(APE_LOADER_LDFLAGS) -o $@ $(patsubst %.lds,-T %.lds,$^)
.PHONY: o/$(MODE)/ape
o/$(MODE)/ape: $(APE_CHECKS) \
o/$(MODE)/ape/ape.o \
o/$(MODE)/ape/ape.lds \
o/$(MODE)/ape/public/ape.lds \
o/$(MODE)/ape/ape.elf \
o/$(MODE)/ape/ape.macho \
o/$(MODE)/ape/ape-copy-self.o \
o/$(MODE)/ape/ape-no-modify-self.o
endif
# these assembly files are safe to build on aarch64
o/$(MODE)/ape/ape.o: ape/ape.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/ape/ape.o: \
ape/blink-linux-aarch64.gz \
ape/blink-xnu-aarch64.gz
o/$(MODE)/ape/ape.lds: \
ape/ape.lds \
ape/macros.internal.h \
ape/relocations.h \
ape/ape.internal.h \
libc/intrin/bits.h \
libc/thread/tls.h \
libc/calls/struct/timespec.h \
libc/thread/thread.h \
libc/dce.h \
libc/elf/def.h \
libc/elf/pf2prot.internal.h \
libc/macros.internal.h \
libc/nt/pedef.internal.h \
libc/str/str.h \
libc/zip.internal.h

View file

@ -1,107 +1,57 @@
#!/bin/sh #!/bin/sh
PROG=${0##*/}
MODE=${MODE:-$m}
TMPDIR=${TMPDIR:-/tmp}
COSMO=${COSMO:-/opt/cosmo}
COSMOS=${COSMOS:-/opt/cosmos}
if [ ! -f ape/loader.c ]; then
cd "$COSMO" || exit
fi
if [ -x .cosmocc/current/bin/make ]; then
MAKE=.cosmocc/current/bin/make
else
MAKE=make
fi
if [ "$(id -u)" -eq 0 ]; then if [ "$(id -u)" -eq 0 ]; then
SUDO= SUDO=
elif command -v sudo >/dev/null 2>&1; then else
SUDO=sudo SUDO=sudo
elif command -v doas >/dev/null 2>&1; then
SUDO=doas
else
echo "need root or sudo" >&2
exit
fi
if command -v install >/dev/null 2>&1; then
if [ x"$(uname -s)" = xLinux ]; then
INSTALL="install -o root -g root -m 755"
else
INSTALL="install -o root -g wheel -m 755"
fi
else
INSTALL="cp -f"
fi fi
echo "Actually Portable Executable (APE) Installer" >&2 echo "Actually Portable Executable (APE) Installer" >&2
echo "Author: Justine Tunney <jtunney@gmail.com>" >&2 echo "Author: Justine Tunney <jtunney@gmail.com>" >&2
# special installation process for apple silicon
if [ x"$(uname -s)" = xDarwin ] && [ x"$(uname -m)" = xarm64 ]; then
echo "cc -O -o $TMPDIR/ape.$$ ape/ape-m1.c" >&2
cc -O -o "$TMPDIR/ape.$$" ape/ape-m1.c || exit
trap 'rm "$TMPDIR/ape.$$"' EXIT
if [ ! -d /usr/local/bin ]; then
echo "$SUDO mkdir -p /usr/local/bin" >&2
$SUDO mkdir -p /usr/local/bin || exit
fi
echo "$SUDO $INSTALL $TMPDIR/ape.$$ /usr/local/bin/ape" >&2
$SUDO $INSTALL "$TMPDIR/ape.$$" /usr/local/bin/ape || exit
exit
fi
if [ x"$(uname -m)" = xarm64 ] || [ x"$(uname -m)" = xaarch64 ]; then
MODE=aarch64
EXT=elf
BEXT=aarch64
elif [ x"$(uname -m)" = xx86_64 ]; then
MODE=
if [ x"$(uname -s)" = xDarwin ]; then
EXT=macho
else
EXT=elf
fi
BEXT=$EXT
else
echo "unsupported architecture $(uname -m)" >&2
exit
fi
################################################################################ ################################################################################
# INSTALL APE LOADER SYSTEMWIDE # INSTALL APE LOADER SYSTEMWIDE
if [ -f o/$MODE/depend ] && $MAKE -j8 o/$MODE/ape; then if [ -f o/depend ] && make -j8 o//ape; then
echo "successfully recompiled ape loader" >&2 echo "successfully recompiled ape loader" >&2
elif [ -x o/$MODE/ape/ape.$EXT ]; then
echo "using ape loader you compiled earlier" >&2
elif [ -d build/bootstrap ]; then elif [ -d build/bootstrap ]; then
# if make isn't being used then it's unlikely the user changed the sources # if make isn't being used then it's unlikely the user changed the sources
# in that case the prebuilt binaries should be completely up-to-date # in that case the prebuilt binaries should be completely up-to-date
echo "using prebuilt ape loader from cosmo repo" >&2 echo "using prebuilt ape loader from cosmo repo" >&2
mkdir -p o/$MODE/ape || exit mkdir -p o//ape || exit
cp -af build/bootstrap/ape.$BEXT o/$MODE/ape/ape.$EXT || exit cp -af build/bootstrap/ape.elf o//ape/ape.elf || exit
cp -af build/bootstrap/ape.macho o//ape/ape.macho || exit
else else
echo "no cosmopolitan libc repository here" >&2 echo "no cosmopolitan libc repository here" >&2
echo "fetching ape loader from justine.lol" >&2 echo "fetching ape loader from justine.lol" >&2
mkdir -p o/$MODE/ape || exit mkdir -p o//ape || exit
if command -v wget >/dev/null 2>&1; then if command -v wget >/dev/null 2>&1; then
wget -qO o/$MODE/ape/ape.$EXT https://justine.lol/ape.$BEXT || exit wget -qO o//ape/ape.elf https://justine.lol/ape.elf || exit
wget -qO o//ape/ape.macho https://justine.lol/ape.macho || exit
else else
curl -Rso o/$MODE/ape/ape.$EXT https://justine.lol/ape.$BEXT || exit curl -Rso o//ape/ape.elf https://justine.lol/ape.elf || exit
curl -Rso o//ape/ape.macho https://justine.lol/ape.macho || exit
fi fi
chmod +x o/$MODE/ape/ape.$EXT || exit chmod +x o//ape/ape.elf || exit
chmod +x o//ape/ape.macho || exit
fi fi
if ! [ /usr/bin/ape -nt o/$MODE/ape/ape.$EXT ]; then if [ "$(uname -s)" = "Darwin" ]; then
echo >&2 if ! [ /usr/bin/ape -nt o//ape/ape.macho ]; then
echo "installing o/$MODE/ape/ape.$EXT to /usr/bin/ape" >&2 echo >&2
echo "$SUDO $INSTALL o/$MODE/ape/ape.$EXT /usr/bin/ape" >&2 echo "installing o//ape/ape.elf to /usr/bin/ape" >&2
$SUDO $INSTALL o/$MODE/ape/ape.$EXT /usr/bin/ape || exit echo "$SUDO mv -f o//ape/ape.elf /usr/bin/ape" >&2
echo "done" >&2 $SUDO cp -f o//ape/ape.macho /usr/bin/ape || exit
echo "done" >&2
fi
else
if ! [ /usr/bin/ape -nt o//ape/ape.elf ]; then
echo >&2
echo "installing o//ape/ape.elf to /usr/bin/ape" >&2
echo "$SUDO mv -f o//ape/ape.elf /usr/bin/ape" >&2
$SUDO cp -f o//ape/ape.elf /usr/bin/ape || exit
echo "done" >&2
fi
fi fi
################################################################################ ################################################################################
@ -137,20 +87,13 @@ if [ x"$(uname -s)" = xLinux ]; then
echo done >&2 echo done >&2
fi fi
uname_r="$(uname -r)"
if printf '%s\n%s\n' 5.12 "$uname_r" | sort -CV; then
FLAGS=FP
else
FLAGS=F
fi
echo >&2 echo >&2
echo registering APE with binfmt_misc >&2 echo registering APE with binfmt_misc >&2
echo you may need to edit configs to persist across reboot >&2 echo you may need to edit configs to persist across reboot >&2
echo '$SUDO sh -c "echo '"'"':APE:M::MZqFpD::/usr/bin/ape:'"$FLAGS'"' >/proc/sys/fs/binfmt_misc/register"' >&2 echo '$SUDO sh -c "echo '"'"':APE:M::MZqFpD::/usr/bin/ape:'"'"' >/proc/sys/fs/binfmt_misc/register"' >&2
$SUDO sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:$FLAGS' >/proc/sys/fs/binfmt_misc/register" || exit $SUDO sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" || exit
echo '$SUDO sh -c "echo '"'"':APE-jart:M::jartsr::/usr/bin/ape:'"$FLAGS'"' >/proc/sys/fs/binfmt_misc/register"' >&2 echo '$SUDO sh -c "echo '"'"':APE-jart:M::jartsr::/usr/bin/ape:'"'"' >/proc/sys/fs/binfmt_misc/register"' >&2
$SUDO sh -c "echo ':APE-jart:M::jartsr::/usr/bin/ape:$FLAGS' >/proc/sys/fs/binfmt_misc/register" || exit $SUDO sh -c "echo ':APE-jart:M::jartsr::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" || exit
echo done >&2 echo done >&2
if [ x"$(cat /proc/sys/fs/binfmt_misc/status)" = xdisabled ]; then if [ x"$(cat /proc/sys/fs/binfmt_misc/status)" = xdisabled ]; then

View file

@ -1,23 +1,9 @@
#!/bin/sh #!/bin/sh
PROG=${0##*/}
MODE=${MODE:-$m}
COSMO=${COSMO:-/opt/cosmo}
COSMOS=${COSMOS:-/opt/cosmos}
if [ ! -f ape/loader.c ]; then
cd "$COSMO" || exit
fi
if [ "$UID" = "0" ]; then if [ "$UID" = "0" ]; then
SUDO= SUDO=
elif command -v sudo >/dev/null 2>&1; then
SUDO=sudo
elif command -v doas >/dev/null 2>&1; then
SUDO=doas
else else
echo "need root or sudo" >&2 SUDO=sudo
exit
fi fi
{ {
@ -42,9 +28,6 @@ done
if [ -f /usr/bin/ape ]; then if [ -f /usr/bin/ape ]; then
$SUDO rm -f /usr/bin/ape $SUDO rm -f /usr/bin/ape
fi fi
if [ -f /usr/local/bin/ape ]; then
$SUDO rm -f /usr/local/bin/ape
fi
# legacy installations # legacy installations
rm -f o/tmp/ape /tmp/ape "${TMPDIR:-/tmp}/ape" rm -f o/tmp/ape /tmp/ape "${TMPDIR:-/tmp}/ape"
@ -55,11 +38,8 @@ for x in .ape \
.ape-1.3 \ .ape-1.3 \
.ape-1.4 \ .ape-1.4 \
.ape-1.5 \ .ape-1.5 \
.ape-1.6 \ .ape-blink-0.9.2 \
.ape-1.7 \ .ape-blink-1.0.0; do
.ape-1.8 \
.ape-1.9 \
.ape-1.10; do
rm -f \ rm -f \
~/$x \ ~/$x \
/tmp/$x \ /tmp/$x \

BIN
ape/blink-linux-aarch64.gz Normal file

Binary file not shown.

BIN
ape/blink-xnu-aarch64.gz Executable file

Binary file not shown.

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2020 Justine Alexandra Roberts Tunney
@ -43,10 +43,10 @@
.hidden \fn .hidden \fn
.previous .previous
.section ".idata.ro.ilt.\dll\().2.\actual","a",@progbits .section ".idata.ro.ilt.\dll\().2.\actual","a",@progbits
"idata.ilt.\dll\().\actual": ".Lidata.ilt.\dll\().\actual":
.quad RVA("\dll\().\actual") .quad RVA("\dll\().\actual")
.type "idata.ilt.\dll\().\actual",@object .type ".Lidata.ilt.\dll\().\actual",@object
.size "idata.ilt.\dll\().\actual",.-"idata.ilt.\dll\().\actual" .size ".Lidata.ilt.\dll\().\actual",.-".Lidata.ilt.\dll\().\actual"
.previous .previous
.section ".idata.ro.hnt.\dll\().2.\actual","a",@progbits .section ".idata.ro.hnt.\dll\().2.\actual","a",@progbits
"\dll\().\actual": "\dll\().\actual":
@ -81,7 +81,7 @@
.size ".Lidata.idt.\name",.-".Lidata.idt.\name" .size ".Lidata.idt.\name",.-".Lidata.idt.\name"
.previous .previous
.section ".idata.ro.ilt.\name\().1","aG",@progbits,"\name",comdat .section ".idata.ro.ilt.\name\().1","aG",@progbits,"\name",comdat
.balign __SIZEOF_POINTER__ .align __SIZEOF_POINTER__
.type "idata.ilt.\name",@object .type "idata.ilt.\name",@object
"idata.ilt.\name": "idata.ilt.\name":
.previous/* .previous/*
@ -92,12 +92,12 @@
.quad 0 .quad 0
.previous .previous
.section ".idata.ro.hnt.\name\().1","aG",@progbits,"\name",comdat .section ".idata.ro.hnt.\name\().1","aG",@progbits,"\name",comdat
.balign __SIZEOF_POINTER__ .align __SIZEOF_POINTER__
.type "idata.hnt.\name",@object .type "idata.hnt.\name",@object
.equ "idata.hnt.\name",. .equ "idata.hnt.\name",.
.previous .previous
.section ".piro.data.sort.iat.2.\name\().1","awG",@progbits,"\name",comdat .section ".piro.data.sort.iat.2.\name\().1","awG",@progbits,"\name",comdat
.balign __SIZEOF_POINTER__ .align __SIZEOF_POINTER__
.type "idata.iat.\name",@object .type "idata.iat.\name",@object
"idata.iat.\name": "idata.iat.\name":
.previous/* .previous/*

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2023 Justine Alexandra Roberts Tunney Copyright 2023 Justine Alexandra Roberts Tunney
@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/macros.h" #include "libc/macros.internal.h"
// Calls _start() function of loaded program. // Calls _start() function of loaded program.
// //
@ -31,16 +31,17 @@
// //
// @param rdi is passed through as-is // @param rdi is passed through as-is
// @param rsi is address of entrypoint (becomes zero) // @param rsi is address of entrypoint (becomes zero)
// @param rdx is passed through as-is // @param rdx is stack pointer (becomes zero)
// @param rcx is passed through as-is // @param rcx is passed through as-is
// @param r8 is stack pointer (becomes zero)
// @noreturn // @noreturn
Launch: Launch:
#ifdef __aarch64__ #ifdef __aarch64__
mov x16,x1 mov x16,x1
mov sp,x4 mov sp,x2
mov x1,0 mov x1,0
mov x2,0
mov x3,0
mov x4,0 mov x4,0
mov x5,0 mov x5,0
mov x6,0 mov x6,0
@ -70,7 +71,6 @@ Launch:
#else #else
mov %r8,%rsp
xor %r8d,%r8d xor %r8d,%r8d
xor %r9d,%r9d xor %r9d,%r9d
xor %r10d,%r10d xor %r10d,%r10d
@ -79,6 +79,8 @@ Launch:
xor %r13d,%r13d xor %r13d,%r13d
xor %r14d,%r14d xor %r14d,%r14d
xor %r15d,%r15d xor %r15d,%r15d
mov %rdx,%rsp
xor %edx,%edx
push %rsi push %rsi
xor %esi,%esi xor %esi,%esi
xor %ebp,%ebp xor %ebp,%ebp

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney Copyright 2021 Justine Alexandra Roberts Tunney
@ -16,13 +16,13 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/macho.h" #include "libc/macho.internal.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/macros.h" #include "libc/macros.internal.h"
// Apple Mach-O Executable Headers // Apple Mach-O Executable Headers
// Fixups are applied by objbincopy // Fixups are applied by objbincopy.com
// There must exist a MAC_LC_SEGMENT_64 for every PT_LOAD // There must exist a MAC_LC_SEGMENT_64 for every PT_LOAD
.section .macho,"a",@progbits .section .macho,"a",@progbits

View file

@ -1,5 +1,5 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney Copyright 2021 Justine Alexandra Roberts Tunney
@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "ape/ape.h"
/** /**
* @fileoverview APE Loader for GNU/Systemd/XNU/FreeBSD/NetBSD/OpenBSD * @fileoverview APE Loader for GNU/Systemd/XNU/FreeBSD/NetBSD/OpenBSD
@ -87,8 +86,6 @@
#define MIN(X, Y) ((Y) > (X) ? (X) : (Y)) #define MIN(X, Y) ((Y) > (X) ? (X) : (Y))
#define MAX(X, Y) ((Y) < (X) ? (X) : (Y)) #define MAX(X, Y) ((Y) < (X) ? (X) : (Y))
#define PATH_MAX 1024 /* XXX verify */
#define SupportsLinux() (SUPPORT_VECTOR & LINUX) #define SupportsLinux() (SUPPORT_VECTOR & LINUX)
#define SupportsXnu() (SUPPORT_VECTOR & XNU) #define SupportsXnu() (SUPPORT_VECTOR & XNU)
#define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD) #define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD)
@ -107,53 +104,41 @@
#define IsAarch64() 0 #define IsAarch64() 0
#endif #endif
#ifdef __cplusplus #define O_RDONLY 0
#define EXTERN_C extern "C" #define PROT_NONE 0
#else #define PROT_READ 1
#define EXTERN_C #define PROT_WRITE 2
#endif #define PROT_EXEC 4
#define MAP_SHARED 1
#define O_RDONLY 0 #define MAP_PRIVATE 2
#define PROT_NONE 0 #define MAP_FIXED 16
#define PROT_READ 1 #define MAP_ANONYMOUS (IsLinux() ? 32 : 4096)
#define PROT_WRITE 2 #define MAP_NORESERVE (IsLinux() ? 16384 : 0)
#define PROT_EXEC 4 #define ELFCLASS32 1
#define MAP_SHARED 1 #define ELFDATA2LSB 1
#define MAP_PRIVATE 2 #define EM_NEXGEN32E 62
#define MAP_FIXED 16 #define EM_AARCH64 183
#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096) #define ET_EXEC 2
#define MAP_NORESERVE (IsLinux() ? 16384 : 0) #define ET_DYN 3
#define ELFCLASS32 1 #define PT_LOAD 1
#define ELFDATA2LSB 1 #define PT_DYNAMIC 2
#define EM_NEXGEN32E 62 #define PT_INTERP 3
#define EM_AARCH64 183 #define EI_CLASS 4
#define ET_EXEC 2 #define EI_DATA 5
#define ET_DYN 3 #define PF_X 1
#define PT_LOAD 1 #define PF_W 2
#define PT_DYNAMIC 2 #define PF_R 4
#define PT_INTERP 3 #define AT_PHDR 3
#define EI_CLASS 4 #define AT_PHENT 4
#define EI_DATA 5 #define AT_PHNUM 5
#define PF_X 1 #define AT_PAGESZ 6
#define PF_W 2 #define AT_EXECFN_LINUX 31
#define PF_R 4 #define AT_EXECFN_NETBSD 2014
#define AT_PHDR 3 #define X_OK 1
#define AT_PHENT 4 #define XCR0_SSE 2
#define AT_PHNUM 5 #define XCR0_AVX 4
#define AT_PAGESZ 6 #define PR_SET_MM 35
#define AT_FLAGS 8 #define PR_SET_MM_EXE_FILE 13
#define AT_FLAGS_PRESERVE_ARGV0_BIT 0
#define AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT)
#define AT_EXECFN_LINUX 31
#define AT_EXECFN_NETBSD 2014
#define X_OK 1
#define XCR0_SSE 2
#define XCR0_AVX 4
#define PR_SET_MM 35
#define PR_SET_MM_EXE_FILE 13
#define EF_APE_MODERN 0x101ca75
#define EF_APE_MODERN_MASK 0x1ffffff
#define READ32(S) \ #define READ32(S) \
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \ ((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
@ -169,6 +154,13 @@
(unsigned long)(255 & (S)[1]) << 010 | \ (unsigned long)(255 & (S)[1]) << 010 | \
(unsigned long)(255 & (S)[0]) << 000) (unsigned long)(255 & (S)[0]) << 000)
#define DEBUG(VAR) \
{ \
char ibuf[19] = {0}; \
Utox(ibuf, VAR); \
Print(os, 2, #VAR " ", ibuf, "\n", 0l); \
}
struct ElfEhdr { struct ElfEhdr {
unsigned char e_ident[16]; unsigned char e_ident[16];
unsigned short e_type; unsigned short e_type;
@ -204,52 +196,52 @@ union ElfEhdrBuf {
union ElfPhdrBuf { union ElfPhdrBuf {
struct ElfPhdr phdr; struct ElfPhdr phdr;
char buf[1024]; char buf[4096];
}; };
struct PathSearcher { struct PathSearcher {
int os; int os;
int literally;
const char *name; const char *name;
const char *syspath; const char *syspath;
unsigned long namelen; unsigned long namelen;
char path[PATH_MAX]; char path[1024];
}; };
struct ApeLoader { struct ApeLoader {
union ElfEhdrBuf ehdr;
union ElfPhdrBuf phdr; union ElfPhdrBuf phdr;
struct PathSearcher ps; struct PathSearcher ps;
char path[PATH_MAX]; char path[1024];
}; };
EXTERN_C long SystemCall(long, long, long, long, long, long, long, int); long SystemCall(long, long, long, long, long, long, long, int);
EXTERN_C void Launch(void *, long, void *, int, void *) void Launch(void *, long, void *, int) __attribute__((__noreturn__));
__attribute__((__noreturn__));
extern char __executable_start[]; extern char __executable_start[];
extern char _end[]; extern char _end[];
static int ToLower(int c) {
return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c;
}
static unsigned long StrLen(const char *s) { static unsigned long StrLen(const char *s) {
unsigned long n = 0; unsigned long n = 0;
while (*s++) while (*s++) ++n;
++n;
return n; return n;
} }
static int StrCmp(const char *l, const char *r) { static int StrCmp(const char *l, const char *r) {
unsigned long i = 0; unsigned long i = 0;
while (l[i] == r[i] && r[i]) while (l[i] == r[i] && r[i]) ++i;
++i;
return (l[i] & 255) - (r[i] & 255); return (l[i] & 255) - (r[i] & 255);
} }
#if 0 static const char *BaseName(const char *s) {
int c;
static const char *StrRChr(const char *s, int c) { const char *b = "";
const char *b = 0;
if (s) { if (s) {
for (; *s; ++s) { while ((c = *s++)) {
if (*s == c) { if (c == '/') {
b = s; b = s;
} }
} }
@ -257,13 +249,6 @@ static const char *StrRChr(const char *s, int c) {
return b; return b;
} }
static const char *BaseName(const char *s) {
const char *b = StrRChr(s, '/');
return b ? b + 1 : s;
}
#endif
static void Bzero(void *a, unsigned long n) { static void Bzero(void *a, unsigned long n) {
long z; long z;
char *p, *e; char *p, *e;
@ -338,7 +323,24 @@ static char *GetEnv(char **p, const char *s) {
return 0; return 0;
} }
static char *Utoa(char p[20], unsigned long x) { static char *Utox(char p[19], unsigned long x) {
int i;
if (x) {
*p++ = '0';
*p++ = 'x';
i = (__builtin_clzl(x) ^ (sizeof(long) * 8 - 1)) + 1;
i = (i + 3) & -4;
do {
*p++ = "0123456789abcdef"[(x >> (i -= 4)) & 15];
} while (i);
} else {
*p++ = '0';
}
*p = 0;
return p;
}
static char *Utoa(char p[21], unsigned long x) {
char t; char t;
unsigned long i, a, b; unsigned long i, a, b;
i = 0; i = 0;
@ -358,8 +360,7 @@ static char *Utoa(char p[20], unsigned long x) {
} }
static char *Itoa(char p[21], long x) { static char *Itoa(char p[21], long x) {
if (x < 0) if (x < 0) *p++ = '-', x = -(unsigned long)x;
*p++ = '-', x = -(unsigned long)x;
return Utoa(p, x); return Utoa(p, x);
} }
@ -368,8 +369,7 @@ __attribute__((__noinline__)) static long CallSystem(long arg1, long arg2,
long arg5, long arg6, long arg5, long arg6,
long arg7, int numba, long arg7, int numba,
char os) { char os) {
if (IsXnu()) if (IsXnu()) numba |= 0x2000000;
numba |= 0x2000000;
return SystemCall(arg1, arg2, arg3, arg4, arg5, arg6, arg7, numba); return SystemCall(arg1, arg2, arg3, arg4, arg5, arg6, arg7, numba);
} }
@ -517,62 +517,10 @@ static long Print(int os, int fd, const char *s, ...) {
return Write(fd, b, n, os); return Write(fd, b, n, os);
} }
static long Printf(int os, int fd, const char *fmt, ...) {
int i;
char c;
int k = 0;
unsigned u;
char b[512];
const char *s;
unsigned long d;
__builtin_va_list va;
__builtin_va_start(va, fmt);
for (;;) {
switch ((c = *fmt++)) {
case '\0':
__builtin_va_end(va);
return Write(fd, b, k, os);
case '%':
switch ((c = *fmt++)) {
case 's':
for (s = __builtin_va_arg(va, const char *); s && *s; ++s) {
if (k < 512)
b[k++] = *s;
}
break;
case 'd':
d = __builtin_va_arg(va, unsigned long);
for (i = 16; i--;) {
u = (d >> (i * 4)) & 15;
if (u < 10) {
c = '0' + u;
} else {
u -= 10;
c = 'a' + u;
}
if (k < 512)
b[k++] = c;
}
break;
default:
if (k < 512)
b[k++] = c;
break;
}
break;
default:
if (k < 512)
b[k++] = c;
break;
}
}
}
static void Perror(int os, const char *thing, long rc, const char *reason) { static void Perror(int os, const char *thing, long rc, const char *reason) {
char ibuf[21]; char ibuf[21];
ibuf[0] = 0; ibuf[0] = 0;
if (rc) if (rc) Itoa(ibuf, -rc);
Itoa(ibuf, -rc);
Print(os, 2, "ape error: ", thing, ": ", reason, Print(os, 2, "ape error: ", thing, ": ", reason,
rc ? " failed w/ errno " : "", ibuf, "\n", 0l); rc ? " failed w/ errno " : "", ibuf, "\n", 0l);
} }
@ -583,17 +531,38 @@ __attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc,
Exit(127, os); Exit(127, os);
} }
static char AccessCommand(struct PathSearcher *ps, unsigned long pathlen) { static char EndsWithIgnoreCase(const char *p, unsigned long n, const char *s) {
if (pathlen + 1 + ps->namelen + 1 > sizeof(ps->path)) unsigned long i, m;
if (n >= (m = StrLen(s))) {
for (i = n - m; i < n; ++i) {
if (ToLower(p[i]) != *s++) {
return 0;
}
}
return 1;
} else {
return 0; return 0;
if (pathlen && ps->path[pathlen - 1] != '/') }
ps->path[pathlen++] = '/'; }
static char IsComPath(struct PathSearcher *ps) {
return EndsWithIgnoreCase(ps->name, ps->namelen, ".com") ||
EndsWithIgnoreCase(ps->name, ps->namelen, ".exe") ||
EndsWithIgnoreCase(ps->name, ps->namelen, ".com.dbg");
}
static char AccessCommand(struct PathSearcher *ps, const char *suffix,
unsigned long pathlen) {
unsigned long suffixlen;
suffixlen = StrLen(suffix);
if (pathlen + 1 + ps->namelen + suffixlen + 1 > sizeof(ps->path)) return 0;
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/';
MemMove(ps->path + pathlen, ps->name, ps->namelen); MemMove(ps->path + pathlen, ps->name, ps->namelen);
ps->path[pathlen + ps->namelen] = 0; MemMove(ps->path + pathlen + ps->namelen, suffix, suffixlen + 1);
return !Access(ps->path, X_OK, ps->os); return !Access(ps->path, X_OK, ps->os);
} }
static char SearchPath(struct PathSearcher *ps) { static char SearchPath(struct PathSearcher *ps, const char *suffix) {
const char *p; const char *p;
unsigned long i; unsigned long i;
for (p = ps->syspath;;) { for (p = ps->syspath;;) {
@ -602,7 +571,7 @@ static char SearchPath(struct PathSearcher *ps) {
ps->path[i] = p[i]; ps->path[i] = p[i];
} }
} }
if (AccessCommand(ps, i)) { if (AccessCommand(ps, suffix, i)) {
return 1; return 1;
} else if (p[i] == ':') { } else if (p[i] == ':') {
p += i + 1; p += i + 1;
@ -612,25 +581,28 @@ static char SearchPath(struct PathSearcher *ps) {
} }
} }
static char *Commandv(struct PathSearcher *ps, int os, char *name, static char FindCommand(struct PathSearcher *ps, const char *suffix) {
if (MemChr(ps->name, '/', ps->namelen)) {
ps->path[0] = 0;
return AccessCommand(ps, suffix, 0);
}
return SearchPath(ps, suffix);
}
static char *Commandv(struct PathSearcher *ps, int os, const char *name,
const char *syspath) { const char *syspath) {
if (!(ps->namelen = StrLen((ps->name = name))))
return 0;
if (ps->literally || MemChr(ps->name, '/', ps->namelen))
return name;
ps->os = os; ps->os = os;
ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin"; ps->syspath = syspath ? syspath : "/bin:/usr/local/bin:/usr/bin";
if (ps->namelen + 1 > sizeof(ps->path)) if (!(ps->namelen = StrLen((ps->name = name)))) return 0;
return 0; if (ps->namelen + 1 > sizeof(ps->path)) return 0;
ps->path[0] = 0; if (FindCommand(ps, "") || (!IsComPath(ps) && FindCommand(ps, ".com"))) {
if (SearchPath(ps)) {
return ps->path; return ps->path;
} else { } else {
return 0; return 0;
} }
} }
__attribute__((__noreturn__)) static void Spawn(int os, char *exe, int fd, __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
long *sp, unsigned long pagesz, long *sp, unsigned long pagesz,
struct ElfEhdr *e, struct ElfEhdr *e,
struct ElfPhdr *p) { struct ElfPhdr *p) {
@ -650,6 +622,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, char *exe, int fd,
found_code = 0; found_code = 0;
found_entry = 0; found_entry = 0;
virtmin = virtmax = 0; virtmin = virtmax = 0;
if (!pagesz) pagesz = 4096;
if (pagesz & (pagesz - 1)) { if (pagesz & (pagesz - 1)) {
Pexit(os, exe, 0, "AT_PAGESZ isn't two power"); Pexit(os, exe, 0, "AT_PAGESZ isn't two power");
} }
@ -678,8 +651,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, char *exe, int fd,
Pexit(os, exe, 0, "ELF segments overlap your APE loader"); Pexit(os, exe, 0, "ELF segments overlap your APE loader");
} }
for (j = i + 1; j < e->e_phnum; ++j) { for (j = i + 1; j < e->e_phnum; ++j) {
if (p[j].p_type != PT_LOAD) if (p[j].p_type != PT_LOAD) continue;
continue;
c = p[j].p_vaddr & -pagesz; c = p[j].p_vaddr & -pagesz;
d = (p[j].p_vaddr + p[j].p_memsz + (pagesz - 1)) & -pagesz; d = (p[j].p_vaddr + p[j].p_memsz + (pagesz - 1)) & -pagesz;
if (MAX(a, c) < MIN(b, d)) { if (MAX(a, c) < MIN(b, d)) {
@ -712,8 +684,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, char *exe, int fd,
if (e->e_type == ET_DYN) { if (e->e_type == ET_DYN) {
rc = Mmap(0, virtmax - virtmin, PROT_NONE, rc = Mmap(0, virtmax - virtmin, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0, os); MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0, os);
if (rc < 0) if (rc < 0) Pexit(os, exe, rc, "pie mmap");
Pexit(os, exe, rc, "pie mmap");
dynbase = rc; dynbase = rc;
if (dynbase & (pagesz - 1)) { if (dynbase & (pagesz - 1)) {
Pexit(os, exe, 0, "OS mmap incongruent w/ AT_PAGESZ"); Pexit(os, exe, 0, "OS mmap incongruent w/ AT_PAGESZ");
@ -727,70 +698,48 @@ __attribute__((__noreturn__)) static void Spawn(int os, char *exe, int fd,
/* load elf */ /* load elf */
for (i = 0; i < e->e_phnum; ++i) { for (i = 0; i < e->e_phnum; ++i) {
void *addr; if (p[i].p_type != PT_LOAD) continue;
unsigned long size;
if (p[i].p_type != PT_LOAD)
continue;
/* configure mapping */ /* configure mapping */
prot = 0; prot = 0;
flags = MAP_FIXED | MAP_PRIVATE; flags = MAP_FIXED | MAP_PRIVATE;
if (p[i].p_flags & PF_R) if (p[i].p_flags & PF_R) prot |= PROT_READ;
prot |= PROT_READ; if (p[i].p_flags & PF_W) prot |= PROT_WRITE;
if (p[i].p_flags & PF_W) if (p[i].p_flags & PF_X) prot |= PROT_EXEC;
prot |= PROT_WRITE;
if (p[i].p_flags & PF_X)
prot |= PROT_EXEC;
/* load from file */
if (p[i].p_filesz) { if (p[i].p_filesz) {
/* load from file */ void *addr;
int prot1, prot2; int prot1, prot2;
unsigned long wipe; unsigned long size;
prot1 = prot; prot1 = prot;
prot2 = prot; prot2 = prot;
/* when we ask the system to map the interval [vaddr,vaddr+filesz)
it might schlep extra file content into memory on both the left
and the righthand side. that's because elf doesn't require that
either side of the interval be aligned on the system page size.
normally we can get away with ignoring these junk bytes. but if
the segment defines bss memory (i.e. memsz > filesz) then we'll
need to clear the extra bytes in the page, if they exist. since
we can't do that if we're mapping a read-only page, we can just
map it with write permissions and call mprotect on it afterward */
a = p[i].p_vaddr + p[i].p_filesz; /* end of file content */ a = p[i].p_vaddr + p[i].p_filesz; /* end of file content */
b = (a + (pagesz - 1)) & -pagesz; /* first pure bss page */ b = (a + (pagesz - 1)) & -pagesz; /* first pure bss page */
c = p[i].p_vaddr + p[i].p_memsz; /* end of segment data */ c = p[i].p_vaddr + p[i].p_memsz; /* end of segment data */
wipe = MIN(b - a, c - a); if (b > c) b = c;
if (wipe && (~prot1 & PROT_WRITE)) { if (c > b && (~prot1 & PROT_WRITE)) {
prot1 = PROT_READ | PROT_WRITE; prot1 = PROT_READ | PROT_WRITE;
} }
addr = (void *)(dynbase + (p[i].p_vaddr & -pagesz)); addr = (void *)(dynbase + (p[i].p_vaddr & -pagesz));
size = (p[i].p_vaddr & (pagesz - 1)) + p[i].p_filesz; size = (p[i].p_vaddr & (pagesz - 1)) + p[i].p_filesz;
rc = Mmap(addr, size, prot1, flags, fd, p[i].p_offset & -pagesz, os); rc = Mmap(addr, size, prot1, flags, fd, p[i].p_offset & -pagesz, os);
if (rc < 0) if (rc < 0) Pexit(os, exe, rc, "prog mmap");
Pexit(os, exe, rc, "prog mmap"); if (c > b) Bzero((void *)(dynbase + a), b - a);
if (wipe)
Bzero((void *)(dynbase + a), wipe);
if (prot2 != prot1) { if (prot2 != prot1) {
rc = Mprotect(addr, size, prot2, os); rc = Mprotect(addr, size, prot2, os);
if (rc < 0) if (rc < 0) Pexit(os, exe, rc, "prog mprotect");
Pexit(os, exe, rc, "prog mprotect");
} }
/* allocate extra bss */ }
if (c > b) {
flags |= MAP_ANONYMOUS; /* allocate extra bss */
rc = Mmap((void *)(dynbase + b), c - b, prot, flags, -1, 0, os); a = p[i].p_vaddr + p[i].p_filesz;
if (rc < 0) a = (a + (pagesz - 1)) & -pagesz;
Pexit(os, exe, rc, "extra bss mmap"); b = p[i].p_vaddr + p[i].p_memsz;
} if (b > a) {
} else {
/* allocate pure bss */
addr = (void *)(dynbase + (p[i].p_vaddr & -pagesz));
size = (p[i].p_vaddr & (pagesz - 1)) + p[i].p_memsz;
flags |= MAP_ANONYMOUS; flags |= MAP_ANONYMOUS;
rc = Mmap(addr, size, prot, flags, -1, 0, os); rc = Mmap((void *)(dynbase + a), b - a, prot, flags, -1, 0, os);
if (rc < 0) if (rc < 0) Pexit(os, exe, rc, "bss mmap");
Pexit(os, exe, rc, "bss mmap");
} }
} }
@ -799,27 +748,25 @@ __attribute__((__noreturn__)) static void Spawn(int os, char *exe, int fd,
Msyscall(dynbase + code, codesize, os); Msyscall(dynbase + code, codesize, os);
/* call program entrypoint */ /* call program entrypoint */
Launch(IsFreebsd() ? sp : 0, dynbase + e->e_entry, exe, os, sp); Launch(IsFreebsd() ? sp : 0, dynbase + e->e_entry, sp, os);
} }
static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf, static const char *TryElf(struct ApeLoader *M, const char *exe, int fd,
char *exe, int fd, long *sp, long *auxv, long *sp, long *auxv, unsigned long pagesz, int os) {
unsigned long pagesz, int os) {
long i, rc; long i, rc;
unsigned size; unsigned size;
struct ElfEhdr *e; struct ElfEhdr *e;
struct ElfPhdr *p; struct ElfPhdr *p;
/* validate page size */ /* validate page size */
if (!pagesz) if (!pagesz) pagesz = 4096;
pagesz = 4096;
if (pagesz & (pagesz - 1)) { if (pagesz & (pagesz - 1)) {
Pexit(os, exe, 0, "AT_PAGESZ isn't two power"); Pexit(os, exe, 0, "AT_PAGESZ isn't two power");
} }
/* validate elf header */ /* validate elf header */
e = &ebuf->ehdr; e = &M->ehdr.ehdr;
if (READ32(ebuf->buf) != READ32("\177ELF")) { if (READ32(M->ehdr.buf) != READ32("\177ELF")) {
return "didn't embed ELF magic"; return "didn't embed ELF magic";
} }
if (e->e_ident[EI_CLASS] == ELFCLASS32) { if (e->e_ident[EI_CLASS] == ELFCLASS32) {
@ -837,10 +784,6 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
return "couldn't find ELF header with x86-64 machine type"; return "couldn't find ELF header with x86-64 machine type";
} }
#endif #endif
if ((e->e_flags & EF_APE_MODERN_MASK) != EF_APE_MODERN && sp[0] > 0) {
/* change argv[0] to resolved path for older binaries */
((char **)(sp + 1))[0] = exe;
}
if (e->e_phentsize != sizeof(struct ElfPhdr)) { if (e->e_phentsize != sizeof(struct ElfPhdr)) {
Pexit(os, exe, 0, "e_phentsize is wrong"); Pexit(os, exe, 0, "e_phentsize is wrong");
} }
@ -851,10 +794,8 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
/* read program headers */ /* read program headers */
rc = Pread(fd, M->phdr.buf, size, e->e_phoff, os); rc = Pread(fd, M->phdr.buf, size, e->e_phoff, os);
if (rc < 0) if (rc < 0) return "failed to read ELF program headers";
return "failed to read ELF program headers"; if (rc != size) return "truncated read of ELF program headers";
if (rc != size)
return "truncated read of ELF program headers";
/* bail on recoverable program header errors */ /* bail on recoverable program header errors */
p = &M->phdr.phdr; p = &M->phdr.phdr;
@ -929,37 +870,39 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
Spawn(os, exe, fd, sp, pagesz, e, p); Spawn(os, exe, fd, sp, pagesz, e, p);
} }
__attribute__((__noreturn__)) static void ShowUsage(int os, int fd, int rc) { static __attribute__((__noreturn__)) void ShowUsage(int os, int fd, int rc) {
Print(os, fd, Print(os, fd,
"NAME\n" "NAME\n"
"\n" "\n"
" actually portable executable loader version " APE_VERSION_STR "\n" " actually portable executable loader version 1.5\n"
" copyrights 2024 justine alexandra roberts tunney\n" " copyright 2023 justine alexandra roberts tunney\n"
" https://justine.lol/ape.html\n" " https://justine.lol/ape.html\n"
"\n" "\n"
"USAGE\n" "USAGE\n"
"\n" "\n"
" ape PROG [ARGV1,ARGV2,...]\n" " ape [FLAGS] PROG [ARGV1,ARGV2,...]\n"
" ape - PROG [ARGV0,ARGV1,...]\n" " ape [FLAGS] - PROG [ARGV0,ARGV1,...]\n"
"\n"
"FLAGS\n"
"\n"
" -h show this help\n"
" -f force loading of program (do not use execve)\n"
"\n", "\n",
0l); 0l);
Exit(rc, os); Exit(rc, os);
} }
EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, __attribute__((__noreturn__)) //
char dl) { void ApeLoader(long di, long *sp, char dl) {
int rc, n; int rc;
unsigned i; unsigned i, n;
const char *ape;
int c, fd, os, argc; int c, fd, os, argc;
struct ApeLoader *M; struct ApeLoader *M;
char arg0, literally;
unsigned long pagesz; unsigned long pagesz;
union ElfEhdrBuf *ebuf; long *auxv, *ap, *ew;
long *auxv, *ap, *endp, *sp2; char *p, *pe, *exe, *ape, *prog, **argv, **envp;
char *p, *pe, *exe, *prog, **argv, **envp;
(void)Printf; (void)Utox;
/* detect freebsd */ /* detect freebsd */
if (SupportsXnu() && dl == XNU) { if (SupportsXnu() && dl == XNU) {
@ -975,7 +918,7 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
argc = *sp; argc = *sp;
argv = (char **)(sp + 1); argv = (char **)(sp + 1);
envp = (char **)(sp + 1 + argc + 1); envp = (char **)(sp + 1 + argc + 1);
auxv = sp + 1 + argc + 1; auxv = (long *)(sp + 1 + argc + 1);
for (;;) { for (;;) {
if (!*auxv++) { if (!*auxv++) {
break; break;
@ -984,49 +927,51 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
/* determine ape loader program name */ /* determine ape loader program name */
ape = argv[0]; ape = argv[0];
if (!ape) if (!ape) ape = "ape";
ape = "ape";
/* detect openbsd */ /* detect openbsd */
if (SupportsOpenbsd() && !os && !auxv[0]) { if (SupportsOpenbsd() && !os && !auxv[0]) {
os = OPENBSD; os = OPENBSD;
} }
/* xnu passes auxv as an array of strings */
if (os == XNU) {
*auxv = 0;
}
/* detect netbsd and find end of words */ /* detect netbsd and find end of words */
pagesz = 0; pagesz = 0;
arg0 = 0;
for (ap = auxv; ap[0]; ap += 2) { for (ap = auxv; ap[0]; ap += 2) {
if (ap[0] == AT_PAGESZ) { if (ap[0] == AT_PAGESZ) {
pagesz = ap[1]; pagesz = ap[1];
} else if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) { } else if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) {
os = NETBSD; os = NETBSD;
} else if (SupportsLinux() && ap[0] == AT_FLAGS) {
// TODO(mrdomino): maybe set/insert this when we are called as "ape -".
arg0 = !!(ap[1] & AT_FLAGS_PRESERVE_ARGV0);
} }
} }
if (!pagesz) { ew = ap + 1;
pagesz = 4096;
}
endp = ap + 1;
/* the default operating system */ /* allocate loader memory */
n = sizeof(*M) / sizeof(long);
MemMove(sp - n, sp, (char *)ew - (char *)sp);
sp -= n, argv -= n, envp -= n, auxv -= n;
M = (struct ApeLoader *)(ew - n);
/* default operating system */
if (!os) { if (!os) {
os = LINUX; os = LINUX;
} }
/* parse flags */
while (argc > 1) {
if (argv[1][0] != '-') break; /* normal argument */
if (!argv[1][1]) break; /* hyphen argument */
if (!StrCmp(argv[1], "-h") || !StrCmp(argv[1], "--help")) {
ShowUsage(os, 1, 0);
} else {
Print(os, 2, ape, ": invalid flag (pass -h for help)\n", 0l);
Exit(1, os);
}
*++sp = --argc;
++argv;
}
/* we can load via shell, shebang, or binfmt_misc */ /* we can load via shell, shebang, or binfmt_misc */
if (arg0) { if (argc >= 3 && !StrCmp(argv[1], "-")) {
literally = 1;
prog = (char *)sp[2];
argc = sp[2] = sp[0] - 2;
argv = (char **)((sp += 2) + 1);
} else if ((literally = argc >= 3 && !StrCmp(argv[1], "-"))) {
/* if the first argument is a hyphen then we give the user the /* if the first argument is a hyphen then we give the user the
power to change argv[0] or omit it entirely. most operating power to change argv[0] or omit it entirely. most operating
systems don't permit the omission of argv[0] but we do, b/c systems don't permit the omission of argv[0] but we do, b/c
@ -1035,67 +980,41 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
argc = sp[3] = sp[0] - 3; argc = sp[3] = sp[0] - 3;
argv = (char **)((sp += 3) + 1); argv = (char **)((sp += 3) + 1);
} else if (argc < 2) { } else if (argc < 2) {
ShowUsage(os, 2, 1); Print(os, 2, ape, ": missing command name (pass -h for help)\n", 0l);
Exit(1, os);
} else { } else {
if (argv[1][0] == '-') {
rc = !((argv[1][1] == 'h' && !argv[1][2]) ||
!StrCmp(argv[1] + 1, "-help"));
ShowUsage(os, 1 + rc, rc);
}
prog = (char *)sp[2]; prog = (char *)sp[2];
argc = sp[1] = sp[0] - 1; argc = sp[1] = sp[0] - 1;
argv = (char **)((sp += 1) + 1); argv = (char **)((sp += 1) + 1);
} }
/* allocate loader memory in program's arg block */
n = sizeof(struct ApeLoader);
M = (struct ApeLoader *)__builtin_alloca(n);
M->ps.literally = literally;
/* create new bottom of stack for spawned program
system v abi aligns this on a 16-byte boundary
grows down the alloc by poking the guard pages */
n = (endp - sp + 1) * sizeof(long);
sp2 = (long *)__builtin_alloca(n);
if ((long)sp2 & 15)
++sp2;
for (; n > 0; n -= pagesz) {
((char *)sp2)[n - 1] = 0;
}
MemMove(sp2, sp, (endp - sp) * sizeof(long));
argv = (char **)(sp2 + 1);
envp = (char **)(sp2 + 1 + argc + 1);
auxv = sp2 + (auxv - sp);
sp = sp2;
/* allocate ephemeral memory for reading file */
n = sizeof(union ElfEhdrBuf);
ebuf = (union ElfEhdrBuf *)__builtin_alloca(n);
for (; n > 0; n -= pagesz) {
((char *)ebuf)[n - 1] = 0;
}
/* resolve path of executable and read its first page */ /* resolve path of executable and read its first page */
if (!(exe = Commandv(&M->ps, os, prog, GetEnv(envp, "PATH")))) { if (!(exe = Commandv(&M->ps, os, prog, GetEnv(envp, "PATH")))) {
Pexit(os, prog, 0, "not found (maybe chmod +x or ./ needed)"); Pexit(os, prog, 0, "not found (maybe chmod +x or ./ needed)");
} else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) { } else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
Pexit(os, exe, fd, "open"); Pexit(os, exe, fd, "open");
} else if ((rc = Pread(fd, ebuf->buf, sizeof(ebuf->buf), 0, os)) < 0) { } else if ((rc = Pread(fd, M->ehdr.buf, sizeof(M->ehdr.buf), 0, os)) < 0) {
Pexit(os, exe, rc, "read"); Pexit(os, exe, rc, "read");
} else if ((unsigned long)rc < sizeof(ebuf->ehdr)) { } else if ((unsigned long)rc < sizeof(M->ehdr.ehdr)) {
Pexit(os, exe, 0, "too small"); Pexit(os, exe, 0, "too small");
} }
pe = ebuf->buf + rc; pe = M->ehdr.buf + rc;
/* change argv[0] to resolved path if it's ambiguous */
if ((argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) ||
!StrCmp(BaseName(prog), argv[0])) {
argv[0] = exe;
}
/* ape intended behavior /* ape intended behavior
1. if ape, will scan shell script for elf printf statements 1. if ape, will scan shell script for elf printf statements
2. shell script may have multiple lines producing elf headers 2. shell script may have multiple lines producing elf headers
3. all elf printf lines must exist in the first 8192 bytes of file 3. all elf printf lines must exist in the first 8192 bytes of file
4. elf program headers may appear anywhere in the binary */ 4. elf program headers may appear anywhere in the binary */
if (READ64(ebuf->buf) == READ64("MZqFpD='") || if (READ64(M->ehdr.buf) == READ64("MZqFpD='") ||
READ64(ebuf->buf) == READ64("jartsr='") || READ64(M->ehdr.buf) == READ64("jartsr='") ||
READ64(ebuf->buf) == READ64("APEDBG='")) { READ64(M->ehdr.buf) == READ64("APEDBG='")) {
for (p = ebuf->buf; p < pe; ++p) { for (p = M->ehdr.buf; p < pe; ++p) {
if (READ64(p) != READ64("printf '")) { if (READ64(p) != READ64("printf '")) {
continue; continue;
} }
@ -1113,15 +1032,15 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
} }
} }
} }
ebuf->buf[i++] = c; M->ehdr.buf[i++] = c;
if (i >= sizeof(ebuf->buf)) { if (i >= sizeof(M->ehdr.buf)) {
break; break;
} }
} }
if (i >= sizeof(ebuf->ehdr)) { if (i >= sizeof(M->ehdr.ehdr)) {
TryElf(M, ebuf, exe, fd, sp, auxv, pagesz, os); TryElf(M, exe, fd, sp, auxv, pagesz, os);
} }
} }
} }
Pexit(os, exe, 0, TryElf(M, ebuf, exe, fd, sp, auxv, pagesz, os)); Pexit(os, exe, 0, TryElf(M, exe, fd, sp, auxv, pagesz, os));
} }

View file

@ -1,5 +1,5 @@
/*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│ /*-*- mode: ld-script; indent-tabs-mode: nil; tab-width: 2; coding: utf-8 -*-│
vi: set et sts=2 sw=2 fenc=utf-8 :vi vi: set et sts=2 tw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡ ╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ Copyright 2023 Justine Alexandra Roberts Tunney │
│ │ │ │

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2020 Justine Alexandra Roberts Tunney
@ -18,10 +18,123 @@
*/ */
#ifndef APE_MACROS_H_ #ifndef APE_MACROS_H_
#define APE_MACROS_H_ #define APE_MACROS_H_
#include "libc/macros.h" #include "libc/macros.internal.h"
#ifdef __ASSEMBLER__ #ifdef __ASSEMBLER__
/* clang-format off */ /* clang-format off */
/**
* @fileoverview Macros relevant to αcτµαlly pδrταblε εxεcµταblε.
*/
// Calls near (i.e. pc+pcrel<64kB) FUNCTION.
// @mode long,legacy,real
// @cost 9 bytes overhead
.macro rlcall function:req
.byte 0x50 # push %[er]ax
.byte 0xb8,0,0 # mov $?,%[e]ax
jmp 911f
.byte 0x58 # pop %[er]ax
.byte 0xe8 # call Jvds
.long \function\()-.-4
jmp 912f
911: .byte 0x58 # pop %[er]ax
.byte 0xe8 # call Jvds
.short \function\()-.-2
912:
.endm
// Loads far (i.e. <1mb) abs constexpr ADDRESS into ES:DI+EDX+RDX.
// @mode long,legacy,real
.macro movesdi address:req
.byte 0xbf # mov $0x????xxxx,%[e]di
.short \address>>4
.byte 0x8e,0xc7 # mov %di,%es
.byte 0xbf # mov $0x????xxxx,%[e]di
.short \address&0xf
jmp 297f
.byte 0xbf # mov $0x????xxxx,%edi
.long \address
297:
.endm
// Loads 16-bit CONSTEXPR into Qw-register w/ optional zero-extend.
// @mode long,legacy,real
.macro bbmov constexpr:req abcd abcd.hi:req abcd.lo:req
.ifnb \abcd
.if (\constexpr)<128 && (\constexpr)>=0
pushpop \constexpr,\abcd
.exitm
.endif
.endif
movb $(\constexpr)>>8&0xff,\abcd.hi
movb $(\constexpr)&0xff,\abcd.lo
.endm
// Compares 16-bit CONSTEXPR with Qw-register.
// @mode long,legacy,real
.macro bbcmp constexpr:req abcd.hi:req abcd.lo:req
cmpb $(\constexpr)>>8&0xff,\abcd.hi
jnz 387f
cmpb $(\constexpr)&0xff,\abcd.lo
387:
.endm
// Adds 16-bit CONSTEXPR to Qw-register.
// @mode long,legacy,real
.macro bbadd constexpr:req abcd.hi:req abcd.lo:req
addb $(\constexpr)&0xff,\abcd.lo
.if (\constexpr) != 0
adcb $(\constexpr)>>8&0xff,\abcd.hi
.endif
.endm
// Subtracts 16-bit CONSTEXPR from Qw-register.
// @mode long,legacy,real
.macro bbsub constexpr:req abcd.hi:req abcd.lo:req
subb $(\constexpr)&0xff,\abcd.lo
.if (\constexpr) != 0
sbbb $(\constexpr)>>8&0xff,\abcd.hi
.endif
.endm
// Ands Qw-register with 16-bit CONSTEXPR.
// @mode long,legacy,real
.macro bband constexpr:req abcd.hi:req abcd.lo:req
.if ((\constexpr)&0xff) != 0xff || ((\constexpr)>>8&0xff) == 0xff
andb $(\constexpr)&0xff,\abcd.lo
.endif
.if ((\constexpr)>>8&0xff) != 0xff
andb $(\constexpr)>>8&0xff,\abcd.hi
.endif
.endm
// Ors Qw-register with 16-bit CONSTEXPR.
// @mode long,legacy,real
.macro bbor constexpr:req abcd.hi:req abcd.lo:req
.if ((\constexpr)&0xff) != 0 || ((\constexpr)>>8&0xff) != 0
orb $(\constexpr)&0xff,\abcd.lo
.endif
.if ((\constexpr)>>8&0xff) != 0
orb $(\constexpr)>>8&0xff,\abcd.hi
.endif
.endm
// Performs ACTION only if in real mode.
// @mode long,legacy,real
.macro rlo clobber:req action:vararg
990: mov $0,\clobber
.if .-990b!=3
.error "bad clobber or assembler mode"
.endif
991: \action
.rept 2-(.-991b)
nop
.endr
.if .-991b!=2
.error "ACTION must be 1-2 bytes"
.endif
.endm
// Initializes real mode stack. // Initializes real mode stack.
// The most holiest of holy code. // The most holiest of holy code.
// @mode real // @mode real
@ -84,10 +197,9 @@
/* clang-format on */ /* clang-format on */
#elif defined(__LINKER__) #elif defined(__LINKER__)
#define BCX_NIBBLE(X) \ #define BCX_NIBBLE(X) ((((X)&0xf) > 0x9) ? ((X)&0xf) + 0x37 : ((X)&0xf) + 0x30)
((((X) & 0xf) > 0x9) ? ((X) & 0xf) + 0x37 : ((X) & 0xf) + 0x30) #define BCX_OCTET(X) ((BCX_NIBBLE((X) >> 4) << 8) | (BCX_NIBBLE((X) >> 0) << 0))
#define BCX_OCTET(X) ((BCX_NIBBLE((X) >> 4) << 8) | (BCX_NIBBLE((X) >> 0) << 0)) #define BCX_INT16(X) ((BCX_OCTET((X) >> 8) << 16) | (BCX_OCTET((X) >> 0) << 0))
#define BCX_INT16(X) ((BCX_OCTET((X) >> 8) << 16) | (BCX_OCTET((X) >> 0) << 0))
#define BCXSTUB(SYM, X) \ #define BCXSTUB(SYM, X) \
HIDDEN(SYM##_bcx0 = BCX_INT16((X) >> 48)); \ HIDDEN(SYM##_bcx0 = BCX_INT16((X) >> 48)); \
HIDDEN(SYM##_bcx1 = BCX_INT16((X) >> 32)); \ HIDDEN(SYM##_bcx1 = BCX_INT16((X) >> 32)); \
@ -99,12 +211,12 @@
* *
* <p>This allows linker scripts to generate printf commands. * <p>This allows linker scripts to generate printf commands.
*/ */
#define BCO_OCTET(X) (((X) & 0x7) + 0x30) #define BCO_OCTET(X) (((X)&0x7) + 0x30)
#define BCOB_UNIT(X) \ #define BCOB_UNIT(X) \
((BCO_OCTET((X) >> 0) << 24) | (BCO_OCTET((X) >> 3) << 16) | \ ((BCO_OCTET((X) >> 0) << 24) | (BCO_OCTET((X) >> 3) << 16) | \
(BCO_OCTET(((X) & 0xff) >> 6) << 8) | 0x5c) (BCO_OCTET(((X)&0xff) >> 6) << 8) | 0x5c)
#define PFBYTE(SYM, X, I) HIDDEN(SYM##_bcs##I = BCOB_UNIT((X) >> ((I) * 8))) #define PFBYTE(SYM, X, I) HIDDEN(SYM##_bcs##I = BCOB_UNIT((X) >> ((I)*8)))
#define PFSTUB2(SYM, X) \ #define PFSTUB2(SYM, X) \
HIDDEN(SYM = (X)); \ HIDDEN(SYM = (X)); \
PFBYTE(SYM, X, 0); \ PFBYTE(SYM, X, 0); \
@ -141,16 +253,23 @@
: (X) < 10000000 ? BCD_RIGHT(BCD_SMEAR((X) / 1000)) \ : (X) < 10000000 ? BCD_RIGHT(BCD_SMEAR((X) / 1000)) \
: (X) < 100000000 ? BCD_RIGHT(BCD_SMEAR((X) / 10000)) \ : (X) < 100000000 ? BCD_RIGHT(BCD_SMEAR((X) / 10000)) \
: 0xffffffffffffffff) : 0xffffffffffffffff)
#define BCD_RIGHT(X) \ #define BCD_RIGHT(X) \
(((X)) < 10000 ? 0x20202020 \ (((X)) < 10000 ? 0x20202020 \
: (X) < 100000 ? 0x20202030 + (X) % 10 \ : (X) < 100000 ? 0x20202030 + \
: (X) < 1000000 ? 0x20203030 + ((X) / 10) % 10 + (X) % 10 * 0x100 \ (X) % 10 \
: (X) < 10000000 ? 0x20303030 + ((X) / 100) % 10 + \ : (X) < 1000000 ? 0x20203030 + \
((X) / 10) % 10 * 0x100 + (X) % 10 * 0x10000 \ ((X) / 10) % 10 + \
: (X) < 100000000 \ (X) % 10 * 0x100 \
? 0x30303030 + ((X) / 1000) % 10 + ((X) / 100) % 10 * 0x100 + \ : (X) < 10000000 ? 0x20303030 + \
((X) / 10) % 10 * 0x10000 + (X) % 10 * 0x1000000 \ ((X) / 100) % 10 + \
: 0xffffffffffffffff) ((X) / 10) % 10 * 0x100 + \
(X) % 10 * 0x10000 \
: (X) < 100000000 ? 0x30303030 + \
((X) / 1000) % 10 + \
((X) / 100) % 10 * 0x100 + \
((X) / 10) % 10 * 0x10000 + \
(X) % 10 * 0x1000000 \
: 0xffffffffffffffff)
/** /**
* Laying out the GDT entries for a TSS for bare metal operation. * Laying out the GDT entries for a TSS for bare metal operation.
@ -159,11 +278,15 @@
HIDDEN(SYM##_desc_ent0 = TSSDESC_ENT0(BASE, LIM)); \ HIDDEN(SYM##_desc_ent0 = TSSDESC_ENT0(BASE, LIM)); \
HIDDEN(SYM##_desc_ent1 = TSSDESC_ENT1(BASE)); \ HIDDEN(SYM##_desc_ent1 = TSSDESC_ENT1(BASE)); \
ASSERT((LIM) >= 0 && (LIM) <= 0xffff, "bare metal TSS is suspiciously fat") ASSERT((LIM) >= 0 && (LIM) <= 0xffff, "bare metal TSS is suspiciously fat")
#define TSSDESC_ENT0(BASE, LIM) \ #define TSSDESC_ENT0(BASE, LIM) \
(((LIM) << 0 & 0x000000000000ffff) | ((BASE) << 16 & 0x000000ffffff0000) | \ (((LIM) << 0 & 0x000000000000ffff) | \
0x89 << 40 | ((LIM) >> 16 << 48 & 0x000f000000000000) | 0x2 << 52 | \ ((BASE) << 16 & 0x000000ffffff0000) | \
0x89 << 40 | \
((LIM) >> 16 << 48 & 0x000f000000000000) | \
0x2 << 52 | \
((BASE) >> 24 << 56 & 0xff00000000000000)) ((BASE) >> 24 << 56 & 0xff00000000000000))
#define TSSDESC_ENT1(BASE) ((BASE) >> 32 << 0 & 0x00000000ffffffff) #define TSSDESC_ENT1(BASE) \
((BASE) >> 32 << 0 & 0x00000000ffffffff)
#endif /* __ASSEMBLER__ */ #endif /* __ASSEMBLER__ */
#endif /* APE_MACROS_H_ */ #endif /* APE_MACROS_H_ */

View file

@ -1,8 +1,9 @@
#ifndef COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_ #ifndef COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_
#define COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_ #define COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
extern const char __notices[] __attribute__((__weak__)); extern const char __comment_start[] __attribute__((__weak__));
extern unsigned char __executable_start[] __attribute__((__weak__)); extern unsigned char __executable_start[] __attribute__((__weak__));
extern unsigned char __privileged_start[] __attribute__((__weak__)); extern unsigned char __privileged_start[] __attribute__((__weak__));
extern unsigned char _ehead[] __attribute__((__weak__)); extern unsigned char _ehead[] __attribute__((__weak__));
@ -16,17 +17,13 @@ extern unsigned char _tdata_end[] __attribute__((__weak__));
extern unsigned char _tbss_start[] __attribute__((__weak__)); extern unsigned char _tbss_start[] __attribute__((__weak__));
extern unsigned char _tbss_end[] __attribute__((__weak__)); extern unsigned char _tbss_end[] __attribute__((__weak__));
extern unsigned char _tls_align[] __attribute__((__weak__)); extern unsigned char _tls_align[] __attribute__((__weak__));
extern unsigned char _tdata_align[] __attribute__((__weak__));
extern unsigned char _tbss_align[] __attribute__((__weak__));
extern unsigned char __test_start[] __attribute__((__weak__)); extern unsigned char __test_start[] __attribute__((__weak__));
extern unsigned char __ro[] __attribute__((__weak__)); extern unsigned char __ro[] __attribute__((__weak__));
extern unsigned char __data_start[] __attribute__((__weak__)); extern uint8_t __data_start[] __attribute__((__weak__));
extern unsigned char __data_end[] __attribute__((__weak__)); extern uint8_t __data_end[] __attribute__((__weak__));
extern unsigned char __bss_start[] __attribute__((__weak__)); extern uint8_t __bss_start[] __attribute__((__weak__));
extern unsigned char __bss_end[] __attribute__((__weak__)); extern uint8_t __bss_end[] __attribute__((__weak__));
extern unsigned long __got_start[] __attribute__((__weak__));
extern unsigned long __got_end[] __attribute__((__weak__));
extern unsigned char ape_phdrs[] __attribute__((__weak__));
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_ */ #endif /* COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_ */

View file

@ -1,703 +0,0 @@
# Actually Portable Executable Specification v0.1
Actually Portable Executable (APE) is an executable file format that
polyglots the Windows Portable Executable (PE) format with a UNIX Sixth
Edition style shell script that doesn't have a shebang. This makes it
possible to produce a single file binary that executes on the stock
installations of the many OSes and architectures.
## Supported OSes and Architectures
- AMD64
- Linux
- MacOS
- Windows
- FreeBSD
- OpenBSD
- NetBSD
- BIOS
- ARM64
- Linux
- MacOS
- FreeBSD
- Windows (non-native)
## File Header
APE defines three separate file magics, all of which are 8 characters
long. Any file that starts with one of these magic values can be
considered an APE program.
### (1) APE MZ Magic
- ASCII: `MZqFpD='`
- Hex: 4d 5a 71 46 70 44 3d 27
This is the canonical magic used by almost all APE programs. It enables
maximum portability between OSes. When interpreted as a shell script, it
is assigning a single quoted string to an unused variable. The shell
will then ignore subsequent binary content that's placed inside the
string.
It is strongly recommended that this magic value be immediately followed
by a newline (\n or hex 0a) character. Some shells, e.g. FreeBSD SH and
Zsh impose a binary safety check before handing off files that don't
have a shebang to `/bin/sh`. That check applies to the first line, which
can't contain NUL characters.
The letters were carefully chosen so as to be valid x86 instructions in
all operating modes. This makes it possible to store a BIOS bootloader
disk image inside an APE binary. For example, simple CLI programs built
with Cosmopolitan Libc will boot from BIOS into long mode if they're
treated as a floppy disk image.
The letters also allow for the possibility of being treated on x86-64 as
a flat executable, where the PE / ELF / Mach-O executable structures are
ignored, and execution simply begins at the beginning of the file,
similar to how MS-DOS .COM binaries work.
The 0x4a relative offset of the magic causes execution to jump into the
MS-DOS stub defined by Portable Executable. APE binaries built by Cosmo
Libc use tricks in the MS-DOS stub to check the operating mode and then
jump to the appropriate entrypoint, e.g. `_start()`.
#### Decoded as i8086
```asm
dec %bp
pop %dx
jno 0x4a
jo 0x4a
```
#### Decoded as i386
```asm
push %ebp
pop %edx
jno 0x4a
jo 0x4a
```
#### Decoded as x86-64
```asm
rex.WRB
pop %r10
jno 0x4a
jo 0x4a
```
### (2) APE UNIX-Only Magic
- ASCII: `jartsr='`
- Hex: 6a 61 72 74 73 72 3d 27
Being a novel executable format that was first published in 2020, the
APE file format is less understood by industry tools compared to the PE,
ELF, and Mach-O executable file formats, which have been around for
decades. For this reason, APE programs that use the MZ magic above can
attract attention from Windows AV software, which may be unwanted by
developers who aren't interested in targeting the Windows platform.
Therefore the `jartsr='` magic is defined which enables the creation of
APE binaries that can safely target all non-Windows platforms. Even
though this magic is less common, APE interpreters and binfmt-misc
installations MUST support this.
It is strongly recommended that this magic value be immediately followed
by a newline (\n or hex 0a) character. Some shells, e.g. FreeBSD SH and
Zsh impose a binary safety check before handing off files that don't
have a shebang to `/bin/sh`. That check applies to the first line, which
can't contain NUL characters.
The letters were carefully chosen so as to be valid x86 instructions in
all operating modes. This makes it possible to store a BIOS bootloader
disk image inside an APE binary. For example, simple CLI programs built
with Cosmopolitan Libc will boot from BIOS into long mode if they're
treated as a floppy disk image.
The letters also allow for the possibility of being treated on x86-64 as
a flat executable, where the PE / ELF / Mach-O executable structures are
ignored, and execution simply begins at the beginning of the file,
similar to how MS-DOS .COM binaries work.
The 0x78 relative offset of the magic causes execution to jump into the
MS-DOS stub defined by Portable Executable. APE binaries built by Cosmo
Libc use tricks in the MS-DOS stub to check the operating mode and then
jump to the appropriate entrypoint, e.g. `_start()`.
#### Decoded as i8086 / i386 / x86-64
```asm
push $0x61
jb 0x78
jae 0x78
```
### (3) APE Debug Magic
- ASCII: `APEDBG='`
- Hex: 41 50 45 44 42 47 3d 27
While APE files must be valid shell scripts, in practice, UNIX systems
will oftentimes be configured to provide a faster safer alternative to
loading an APE binary through `/bin/sh`. The Linux Kernel can be patched
to have execve() recognize the APE format and directly load its embedded
ELF header. Linux systems can also use binfmt-misc to recognize APE's MZ
and jartsr magic, and pass them to a userspace program named `ape` that
acts as an interpreter. In such environments, the need sometimes arises
to be able to test that the `/bin/sh` is working correctly, in which
case the `APEDBG='` magic is RECOMMENDED.
APE interpreters, execve() implementations, and binfmt-misc installs
MUST ignore this magic. If necessary, steps can be taken to help files
with this magic be passed to `/bin/sh` like a normal shebang-less shell
script for execution.
## Embedded ELF Header
APE binaries MAY embed an ELF header inside them. Unlike conventional
executable file formats, this header is not stored at a fixed offset.
It's instead encoded as octal escape codes in a shell script `printf`
statement. For example:
```
printf '\177ELF\2\1\1\011\0\0\0\0\0\0\0\0\2\0\076\0\1\0\0\0\166\105\100\000\000\000\000\000\060\013\000\000\000\000\000\000\000\000\000\000\000\000\000\000\165\312\1\1\100\0\070\0\005\000\0\0\000\000\000\000'
```
This `printf` statement MUST appear in the first 8192 bytes of the APE
executable, so as to limit how much of the initial portion of a file an
interpreter must load.
Multiple such `printf` statements MAY appear in the first 8192 bytes, in
order to specify multiple architectures. For example, fat binaries built
by the `apelink` program (provided by Cosmo Libc) will have two encoded
ELF headers, for AMD64 and ARM64, each of which point into the proper
file offsets for their respective native code. Therefore, kernels and
interpreters which load the APE format directly MUST check the
`e_machine` field of the `Elf64_Ehdr` that's decoded from the octal
codes, before accepting a `printf` shell statement as valid.
These printf statements MUST always use only unescaped ASCII characters
or octal escape codes. These printf statements MUST NOT use space saving
escape codes such as `\n`. For example, rather than saying `\n` it would
be valid to say `\012` instead. It's also valid to say `\12` but only if
the encoded characters that follow aren't an octal digit.
For example, the following algorithm may be used for parsing octal:
```c
static int ape_parse_octal(const unsigned char page[8192], int i, int *pc)
{
int c;
if ('0' <= page[i] && page[i] <= '7') {
c = page[i++] - '0';
if ('0' <= page[i] && page[i] <= '7') {
c *= 8;
c += page[i++] - '0';
if ('0' <= page[i] && page[i] <= '7') {
c *= 8;
c += page[i++] - '0';
}
}
*pc = c;
}
return i;
}
```
APE aware interpreters SHOULD only take `e_machine` into consideration.
It is the responsibility of the `_start()` function to detect the OS.
Therefore, multiple `printf` statements are only embedded in the shell
script for different CPU architectures.
The OS ABI field of an APE embedded `Elf64_Ehdr` SHOULD be set to
`ELFOSABI_FREEBSD`, since it's the only UNIX OS APE supports that
actually checks the field. However different values MAY be chosen for
binaries that don't intend to have FreeBSD in their support vector.
Counter-intuitively, the ARM64 ELF header is used on the MacOS ARM64
platform when loading from fat binaries.
## Embedded Mach-O Header (x86-64 only)
APE shell scripts that support MacOS on AMD64 must use the `dd` command
in a very specific way to specify how the embedded binary Macho-O header
is copied backward to the start of the file. For example:
```
dd if="$o" of="$o" bs=8 skip=433 count=66 conv=notrunc
```
These `dd` statements have traditionally been generated by the GNU as
and ld.bfd programs by encoding ASCII into 64-bit linker relocations,
which necessitated a fixed width for integer values. It took several
iterations over APE's history before we eventually got it right:
- `arg=" 9293"` is how we originally had ape do it
- `arg=$(( 9293))` b/c busybox sh disliked quoted space
- `arg=9293 ` is generated by modern apelink program
Software that parses the APE file format, which needs to extract the
Macho-O x86-64 header SHOULD support the old binaries that use the
previous encodings. To make backwards compatibility simple the following
regular expression may be used, which generalizes to all defined
formats:
```c
regcomp(&rx,
"bs=" // dd block size arg
"(['\"] *)?" // #1 optional quote w/ space
"(\\$\\(\\( *)?" // #2 optional math w/ space
"([[:digit:]]+)" // #3
"( *\\)\\))?" // #4 optional math w/ space
"( *['\"])?" // #5 optional quote w/ space
" +" //
"skip=" // dd skip arg
"(['\"] *)?" // #6 optional quote w/ space
"(\\$\\(\\( *)?" // #7 optional math w/ space
"([[:digit:]]+)" // #8
"( *\\)\\))?" // #9 optional math w/ space
"( *['\"])?" // #10 optional quote w/ space
" +" //
"count=" // dd count arg
"(['\"] *)?" // #11 optional quote w/ space
"(\\$\\(\\( *)?" // #12 optional math w/ space
"([[:digit:]]+)", // #13
REG_EXTENDED);
```
For further details, see the canonical implementation in
`cosmopolitan/tool/build/assimilate.c`.
## Static Linking
Actually Portable Executables are always statically linked. This
revision of the specification does not define any facility for storing
code in dynamic shared objects.
Cosmopolitan Libc provides a solution that enables APE binaries have
limited access to dlopen(). By manually loading a platform-specific
executable and asking the OS-specific libc's dlopen() to load
OS-specific libraries, it becomes possible to use GPUs and GUIs. This
has worked great for AI projects like llamafile.
There is no way for an Actually Portable Executable to interact with
OS-specific dynamic shared object extension modules to programming
languages. For example, a Lua interpreter compiled as an Actually
Portable Executable would have no way of linking extension libraries
downloaded from the Lua Rocks package manager. This is primarily because
different OSes define incompatible ABIs.
While it was possible to polyglot PE+ELF+MachO to create multi-OS
executables, it simply isn't possible to do that same thing for
DLL+DYLIB+SO. Therefore, in order to have DSOs, APE would need to either
choose one of the existing formats or invent one of its own, and then
develop its own parallel ecosystem of extension software. In the future,
the APE specification may expand to encompass this. However the focus to
date has been exclusively on executables with limited dlopen() support.
## Application Binary Interface (ABI)
APE binaries use the System V ABI, as defined by:
- [System V ABI - AMD64 Architecture Processor Supplement](https://gitlab.com/x86-psABIs/x86-64-ABI)
- AARCH64 has a uniform consensus defined by ARM Limited
There are however a few changes we've had to make.
### No Red Zone
Actually Portable Executables that have Windows and/or bare metal in
their support vector MUST be compiled using `-mno-red-zone`. This is
because, on Windows, DLLs and other software lurking in the va-space
might use tricks like SetThreadContext() to take control of a thread
whereas on bare metal, it's also generally accepted that kernel-mode
code cannot assume a red zone either due to hardware interrupts that
pull the exact same kinds of stunts.
APE software that only has truly System V ABI conformant OSes (e.g.
Linux) in their support vector MAY use the red zone optimization.
### Thread Local Storage
#### aarch64
Here's the TLS memory layout on aarch64:
```
x28
%tpidr_el0
│ _Thread_local
┌───┼───┬──────────┬──────────┐
│tib│dtv│ .tdata │ .tbss │
├───┴───┴──────────┴──────────┘
__get_tls()
```
The ARM64 code in actually portable executables use the `x28` register
to store the address of the thread information block. All aarch64 code
linked into these executables SHOULD be compiled with `-ffixed-x28`
which is supported by both Clang and GCC.
The runtime library for an actually portable executables MAY choose to
use `tpidr_el0` instead, if OSes like MacOS aren't being targeted. For
example, if the goal is to create a Linux-only fat binary linker program
for Musl Libc, then choosing to use the existing `tpidr_el0` convention
would be friction-free alternative.
It's not possible for an APE runtime that targets the full range of OSes
to use the `tpidr_el0` register for TLS because Apple won't allow it. On
MacOS ARM64 systems, this register can only be used by a runtime to
implement the `sched_getcpu()` system call. It's reserved by MacOS.
#### x86-64
Here's the TLS memory layout on x86_64:
```
__get_tls()
%fs OpenBSD/NetBSD
_Thread_local │
┌───┬──────────┬──────────┼───┐
│pad│ .tdata │ .tbss │tib│
└───┴──────────┴──────────┼───┘
Linux/FreeBSD/Windows/Mac %gs
```
Quite possibly the greatest challenge in Actually Portable Executable
working, has been overcoming the incompatibilities between OSes in how
thread-local storage works on x86-64. The AMD64 architecture defines two
special segment registers. Every OS uses one of these segment registers
to implement TLS support. However not all OSes agree on which register
to use. Some OSes grant userspace the power to define either of these
registers to hold any value that is desired. Some OSes only effectively
allow a single one of them to be changed. Lastly, some OSes, e.g.
Windows, claim ownership of the memory layout these registers point
towards too.
Here's a breakdown on how much power is granted to userspace runtimes by
each OS when it comes to changing amd64 segment registers.
| | %fs | %gs |
|---------|--------------|--------------|
| Linux | unrestricted | unrestricted |
| MacOS | inaccessible | unrestricted |
| Windows | inaccessible | restricted |
| FreeBSD | unrestricted | unrestricted |
| NetBSD | unrestricted | broken |
| OpenBSD | unrestricted | inaccessible |
Therefore, regardless of which register one we choose, some OSes are
going to be incompatible.
APE binaries are always built with a Linux compiler. So another issue
arises in the fact that our Linux-flavored GCC and Clang toolchains
(which are used to produce cross-OS binaries) are also only capable of
producing TLS instructions that use the %fs convention.
To solve these challenges, the `cosmocc` compiler will rewrite binary
objects after they've been compiled by GCC, so that the `%gs` register
is used, rather than `%fs`. Morphing x86-64 binaries after they've been
compiled is normally difficult, due to the complexity of the machine
instruction language. However GCC provides `-mno-tls-direct-seg-refs`
which greatly reduces the complexity of this task. This flag forgoes
some optimizations to make the generated code simpler. Rather than doing
clever arithmetic with `%fs` prefixes, the compiler will always generate
the thread information block address load as a separate instruction.
```c
// Change AMD code to use %gs:0x30 instead of %fs:0
// We assume -mno-tls-direct-seg-refs has been used
static void ChangeTlsFsToGs(unsigned char *p, size_t n) {
unsigned char *e = p + n - 9;
while (p <= e) {
// we're checking for the following expression:
// 0144 == p[0] && // %fs
// 0110 == (p[1] & 0373) && // rex.w (and ignore rex.r)
// (0213 == p[2] || // mov reg/mem → reg (word-sized)
// 0003 == p[2]) && // add reg/mem → reg (word-sized)
// 0004 == (p[3] & 0307) && // mod/rm (4,reg,0) means sib → reg
// 0045 == p[4] && // sib (5,4,0) → (rbp,rsp,0) → disp32
// 0000 == p[5] && // displacement (von Neumann endian)
// 0000 == p[6] && // displacement
// 0000 == p[7] && // displacement
// 0000 == p[8] // displacement
uint64_t w = READ64LE(p) & READ64LE("\377\373\377\307\377\377\377\377");
if ((w == READ64LE("\144\110\213\004\045\000\000\000") ||
w == READ64LE("\144\110\003\004\045\000\000\000")) &&
!p[8]) {
p[0] = 0145; // change %fs to %gs
p[5] = 0x30; // change 0 to 0x30
p += 9;
} else {
++p;
}
}
}
```
By favoring `%gs` we've now ensured friction-free compatibility for the
APE runtime on MacOS, Linux, and FreeBSD which are all able to conform
easily to this convention. However additional work needs to be done at
runtime when an APE program is started on Windows, OpenBSD, and NetBSD.
On these platforms, all executable pages must be faulted and morphed to
fixup the TLS instructions.
On OpenBSD and NetBSD, this is as simple as undoing the example
operation above. Earlier at compile-time we turned `%fs` into `%gs`.
Now, at runtime, `%gs` must be turned back into `%fs`. Since the
executable is morphing itself, this is easier said than done.
OpenBSD for example enforces a `W^X` invariant. Code that's executing
can't modify itself at the same time. The way Cosmopolitan solves this
is by defining a special part of the binary called `.text.privileged`.
This section is aligned to page boundaries. A GNU ld linker script is
used to ensure that code which morphs code is placed into this section,
through the use of a header-defined cosmo-specific keyword `privileged`.
Additionally, the `fixupobj` program is used by the Cosmo build system
to ensure that compiled objects don't contain privileged functions that
call non-privileged functions. Needless to say, `mprotect()` needs to be
a privileged function, so that it can be used to disable the execute bit
on all other parts of the executable except for the privileged section,
thereby making it writable. Once this has been done, code can change.
On Windows the displacement bytes of the TLS instruction are changed to
use the `%gs:0x1480+i*8` ABI where `i` is a number assigned by the WIN32
`TlsAlloc()` API. This avoids the need to call `TlsGetValue()` which is
implemented this exact same way under the hood. Even though 0x1480 isn't
explicitly documented by MSDN, this ABI is believed to be stable because
MSVC generates binaries that use this offset directly. The only caveat
is that `TlsAlloc()` must be called as early in the runtime init as
possible, to ensure an index less than 64 is returned.
### Thread Information Block (TIB)
The Actually Portable Executable Thread Information Block (TIB) is
defined by this version of the specification as follows:
- The 64-bit TIB self-pointer is stored at offset 0x00.
- The 64-bit TIB self-pointer is also stored at offset 0x30.
- The 32-bit `errno` value is stored at offset 0x3c.
All other parts of the thread information block should be considered
unspecified and therefore reserved for future specifications.
The APE thread information block is aligned on a 64-byte boundary.
Cosmopolitan Libc v3.5.8 (c. 2024-07-21) currently implements a thread
information block that's 512 bytes in size.
### Foreign Function Calls
Even though APE programs always use the System V ABI, there arises the
occasional need to interface with foreign functions, e.g. WIN32. The
`__attribute__((__ms_abi__))` annotation introduced by GCC v6 is used
for this purpose.
The ability to change a function's ABI on a case-by-case basis is
surprisingly enough supported by GCC, Clang, NVCC, and even the AMD HIP
compilers for both UNIX systems and Windows. All of these compilers
support both the System V ABI and the Microsoft x64 ABI.
APE binaries will actually favor the Microsoft ABI even when running on
UNIX OSes for certain dlopen() use-cases. For example, if we control the
code to a CUDA module, which we compile on each OS separately from our
main APE binary, then any function that's inside the APE binary whose
pointer may be passed into a foreign module SHOULD be compiled to use
the Microsoft ABI. This is because in practice the OS-specific module
may need to be compiled by MSVC, where MS ABI is the *only* ABI, which
forces our UNIX programs to partially conform. Thankfully, all UNIX
compilers support doing it on a case-by-case basis.
### Char Signedness
Actually Portable Executable defines `char` as signed.
Therefore conformant APE software MUST use `-fsigned-char` when building
code for aarch64, as well as any other architecture that (unlike x86-64)
would otherwise define `char` as being `unsigned char` by default.
This decision was one of the cases where it made sense to offer a more
consistent runtime experience for fat multi-arch binaries. However you
SHOULD still write code to assume `char` can go either way. But if all
you care about is using APE, then you CAN assume `char` is signed.
### Long Double
On AMD64 platforms, APE binaries define `long double` as 80-bit.
On ARM64 platforms, APE binaries define `long double` as 128-bit.
We accept inconsistency in this case, because hardware acceleration is
far more valuable than stylistic consistency in the case of mathematics.
One challenge arises on AMD64 for supporting `long double` across OSes.
Unlike UNIX systems, the Windows Executive on x86-64 initializes the x87
FPU to have double (64-bit) precision rather than 80-bit. That's because
code compiled by MSVC treats `long double` as though it were `double` to
prefer always using the more modern SSE instructions. However System V
requires genuine 80-bit `long double` support on AMD64.
Therefore, if an APE program detects that it's been started on a Windows
x86-64 system, then it SHOULD use the following assembly to initialize
the x87 FPU in System V ABI mode.
```asm
fldcw 1f(%rip)
.rodata
.balign 2
// 8087 FPU Control Word
// IM: Invalid Operation ───────────────┐
// DM: Denormal Operand ───────────────┐│
// ZM: Zero Divide ───────────────────┐││
// OM: Overflow ─────────────────────┐│││
// UM: Underflow ───────────────────┐││││
// PM: Precision ──────────────────┐│││││
// PC: Precision Control ───────┐ ││││││
// {float,∅,double,long double}│ ││││││
// RC: Rounding Control ──────┐ │ ││││││
// {even, →-∞, →+∞, →0} │┌┤ ││││││
// ┌┤││ ││││││
// d││││rr││││││
1: .short 0b00000000000000000001101111111
.previous
```
## Executable File Alignment
Actually Portable Executable is a statically-linked flat executable file
format that is, as a thing in itself, agnostic to file alignments. For
example, the shell script payload at the beginning of the file and its
statements have no such requirements. Alignment requirements are however
imposed by the executable formats that APE wraps.
1. ELF requires that file offsets be congruent with virtual addresses
modulo the CPU page size. So when we add a shell script to the start
of an executable, we need to round up to the page size in order to
maintain ELF's invariant. Although no such roundup is required on the
program segments once the invariant is restored. ELF loaders will
happily map program headers from arbitrary file intervals (which may
overlap) onto arbitrarily virtual intervals (which don't need to be
contiguous). In order to do that, the loaders will generally use
UNIX's mmap() function which is more restrictive and only accepts
addresses and offsets that are page aligned. To make it possible to
map an unaligned ELF program header that could potentially start and
stop at any byte, ELF loaders round-out the intervals, which means
adjacent unrelated data might also get mapped, which may need to be
explicitly zero'd. Thanks to the cleverness of ELF, it's possible to
have an executable file be very tiny, without needing any alignment
bytes, and it'll be loaded into a properly aligned virtual space
where segments can be as sparse as we want them to be.
2. PE doesn't care about congruence and instead defines two separate
kinds of alignment. First, PE requires that the layout of segment
memory inside the file be aligned on at minimum the classic 512 byte
MS-DOS page size. This means that, unlike ELF, some alignment padding
may need to be encoded into the file, making it slightly larger. Next
PE imposes an alignment restriction on segments once they've been
mapped into the virtual address space, which must be rounded to the
system page size. Like ELF, PE segments need to be properly ordered
but they're allowed to drift apart once mapped in a non-contiguous
sparsely mapped way. When inserting shell script content at the start
of a PE file, the most problematic thing is the need to round up to
the 64kb system granularity, which results in a lot of needless bytes
of padding being inserted by a naive second-pass linker.
3. Apple's Mach-O format is the strictest of them all. While both ELF
and PE are defined in such a way that invites great creativity, XNU
will simply refuse to an executable that does anything creative with
alignment. All loaded segments need to both start and end on a page
aligned address. XNU also wants segments to be contiguous similar to
portable executable, except it applies to both the file and virtual
spaces, which must follow the same structure.
Actually Portable Executables must conform to the strictest requirements
demanded by the support vector. Therefore an APE binary that has headers
for all three of the above executable formats MUST conform to the Apple
way of doing things. GNU ld linker scripts aren't very good at producing
ELF binaries that rigidly conform to this simple naive layout. There are
so many ways things can go wrong, where third party code might slip its
own custom section name in-between the linker script sections that are
explicitly defined, thereby causing ELF's powerful features to manifest
and the resulting content overlapping. The best `ld` flag that helps is
`--orphan-handling=error` which can help with explaining such mysteries.
While Cosmopolitan was originally defined to just use stock GNU tools,
this proved intractable over time, and the project has been evolving in
the direction of building its own. Inventing the `apelink` program was
what enabled the project to achieve multi-architecture binaries whereas
previously it was only possible to do multi-OS binaries. In the future,
our hope is that a fast power linker like Mold can be adapted to produce
fat APE binaries directly from object files in one pass.
## Position Independent Code
APE doesn't currently support position independent executable formats.
This is because APE was originally written for the GNU linker, where PIC
and PIE were after-thoughts and never fully incorporated with the older
more powerful linker script techniques upon which APE relies. Future
iterations of this specification are intended to converge on modern
standards, as our tooling becomes developed enough to support it.
However this only applies to the wrapped executable formats themselves.
While our convention to date has been to always load ELF programs at the
4mb mark, this is not guaranteed across OSes and architectures. Programs
should have no expectations that a program will be loaded to any given
address. For example, Cosmo currently implements APE on AARCH64 as
loading executables to a starting address of 0x000800000000. This
address occupies a sweet spot of requirements.
## Address Space
In order to create a single binary that supports as many platforms as
possible without needing to be recompiled, there's a very narrow range
of addresses that can be used. That range is somewhere between 32 bits
and 39 bits.
- Embedded devices that claim to be 64-bit will oftentimes only support
a virtual address space that's 39 bits in size.
- We can't load executable images on AARCH64 beneath 0x100000000 (4gb)
because Apple forbids doing that, possibly in an effort to enforce a
best practice for spotting 32-bit to 64-bit transition bugs. Please
note that this restriction only applies to Apple ARM64 systems. The
x86-64 version of XNU will happily load APE binaries to 0x00400000.
- The AMD64 architecture on desktops and servers can usually be counted
upon to provide a 47-bit address space. The Linux Kernel for instance
grants each userspace program full dominion over addresses 0x00200000
through 0x00007fffffffffff provided the hardware supports this. On
modern workstations supporting Intel and AMD's new PML5T feature which
virtualizes memory using a radix trie that's five layers deep, Linux
is able to offer userspace its choice of fixed addresses from
0x00200000 through 0x00ffffffffffffff. The only exception to this rule
we've encountered so far is that Windows 7 and Windows Vista behaved
similar to embedded devices in reducing the number of va bits.
## Page Size
APE software MUST be page size agnostic. For many years the industry had
converged on a strong consensus of having a page size that's 4096 bytes.
However this convention was never guaranteed. New computers have become
extremely popular, such as Apple Silicon, that use a 16kb page size.
By convention, Cosmopolitan Libc currently generates ELF headers for
x86-64 that are strictly aligned on a 4096-byte page size. On ARM64
Cosmopolitan is currently implemented to always generate ELF headers
aligned on a 16kb page size.
In addition to being page size agnostic, APE software that cares about
working correctly on Windows needs to be aware of the concept of
allocation granularity. While the page size on Windows is generally 4kb
in size, memory mappings can only be created on addresses that aligned
to the system allocation granularity, which is generally 64kb. If you
use a function like mmap() with Cosmopolitan Libc, then the `addr` and
`offset` parameters need to be aligned to `sysconf(_SC_GRANSIZE)` or
else your software won't work on Windows. Windows has other limitations
too, such as lacking the ability to carve or punch holes in mappings.

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2023 Justine Alexandra Roberts Tunney Copyright 2023 Justine Alexandra Roberts Tunney
@ -17,13 +17,11 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/dce.h" #include "libc/dce.h"
#include "ape/ape.h" #include "libc/macros.internal.h"
#include "libc/macros.h"
#ifdef __aarch64__ #ifdef __aarch64__
_start: mov x1,sp _start: mov x1,sp
and sp,x1,#-16
mov x29,0 mov x29,0
bl ApeLoader bl ApeLoader
.endfn _start,globl .endfn _start,globl
@ -34,7 +32,7 @@ XnuEntrypoint:
mov $_HOSTXNU,%dl // xnu's not unix! mov $_HOSTXNU,%dl // xnu's not unix!
ElfEntrypoint: ElfEntrypoint:
mov %rsp,%rsi // save real stack mov %rsp,%rsi // save real stack
andq $-16,%rsp // force SSE alignment sub $1024*1024,%rsp // room for allocs
call ApeLoader call ApeLoader
.endfn ElfEntrypoint,globl .endfn ElfEntrypoint,globl
.endfn XnuEntrypoint,globl .endfn XnuEntrypoint,globl
@ -67,7 +65,7 @@ ape.ident:
.long 1 .long 1
1: .asciz "APE" 1: .asciz "APE"
2: .balign 4 2: .balign 4
3: .long APE_VERSION_NOTE 3: .long 105000000
4: .size ape.ident,.-ape.ident 4: .size ape.ident,.-ape.ident
.type ape.ident,@object .type ape.ident,@object

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2023 Justine Alexandra Roberts Tunney Copyright 2023 Justine Alexandra Roberts Tunney
@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/macros.h" #include "libc/macros.internal.h"
// Invokes system call. // Invokes system call.
// //

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
build/bootstrap/ape.silicon Executable file

Binary file not shown.

BIN
build/bootstrap/ar.com Executable file

Binary file not shown.

BIN
build/bootstrap/chmod.com Executable file

Binary file not shown.

Binary file not shown.

BIN
build/bootstrap/cocmd.com Executable file

Binary file not shown.

BIN
build/bootstrap/compile.com Executable file

Binary file not shown.

BIN
build/bootstrap/cp.com Executable file

Binary file not shown.

BIN
build/bootstrap/echo.com Executable file

Binary file not shown.

BIN
build/bootstrap/fixupobj.com Executable file

Binary file not shown.

View file

@ -1,58 +0,0 @@
--nocompress-debug-sections
--noexecstack
-Wa,--nocompress-debug-sections
-Wa,--noexecstack
-Wa,-msse2avx
-Werror=maybe-uninitialized
-Wno-literal-suffix
-Wno-unused-but-set-variable
-Wunsafe-loop-optimizations
-fbranch-target-load-optimize
-fcx-limited-range
-fdelete-dead-exceptions
-femit-struct-debug-baseonly
-ffp-int-builtin-inexact
-finline-functions-called-once
-fipa-pta
-fivopts
-flimit-function-alignment
-fmerge-constants
-fmodulo-sched
-fmodulo-sched-allow-regmoves
-fno-align-jumps
-fno-align-labels
-fno-align-loops
-fno-code-hoisting
-fno-cx-limited-range
-fno-fp-int-builtin-inexact
-fno-gnu-unique
-fno-inline-functions-called-once
-fno-instrument-functions
-fno-schedule-insns2
-fno-whole-program
-fopt-info-vec
-fopt-info-vec-missed
-freg-struct-return
-freschedule-modulo-scheduled-loops
-frounding-math
-fsched2-use-superblocks
-fschedule-insns
-fschedule-insns2
-fshrink-wrap
-fshrink-wrap-separate
-fsignaling-nans
-fstack-clash-protection
-ftracer
-ftrapv
-ftree-loop-im
-ftree-loop-vectorize
-funsafe-loop-optimizations
-fversion-loops-for-strides
-fwhole-program
-gdescribe-dies
-gstabs
-mcall-ms2sysv-xlogues
-mdispatch-scheduler
-mfpmath=sse+387
-mmitigate-rop
-mno-fentry

BIN
build/bootstrap/gzip.com Executable file

Binary file not shown.

BIN
build/bootstrap/make.com Executable file

Binary file not shown.

BIN
build/bootstrap/mkdeps.com Executable file

Binary file not shown.

BIN
build/bootstrap/mkdir.com Executable file

Binary file not shown.

BIN
build/bootstrap/objbincopy.com Executable file

Binary file not shown.

BIN
build/bootstrap/package.com Executable file

Binary file not shown.

BIN
build/bootstrap/pwd.com Executable file

Binary file not shown.

BIN
build/bootstrap/rm.com Executable file

Binary file not shown.

BIN
build/bootstrap/touch.com Executable file

Binary file not shown.

BIN
build/bootstrap/unbundle.com Executable file

Binary file not shown.

BIN
build/bootstrap/zipcopy.com Executable file

Binary file not shown.

BIN
build/bootstrap/zipobj.com Executable file

Binary file not shown.

View file

@ -1,5 +1,5 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ #-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set et ft=make ts=8 sw=8 fenc=utf-8 :vi ──────────────────────┘ #───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
# Default Mode # Default Mode
# #
@ -11,19 +11,14 @@
# #
ifeq ($(MODE),) ifeq ($(MODE),)
ENABLE_FTRACE = 1 ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
CONFIG_CCFLAGS += -O2 $(BACKTRACES)
CONFIG_CPPFLAGS += -DSYSDEBUG
endif
ifeq ($(MODE), x86_64)
ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CCFLAGS += -O2 $(BACKTRACES) CONFIG_CCFLAGS += -O2 $(BACKTRACES)
CONFIG_CPPFLAGS += -DSYSDEBUG CONFIG_CPPFLAGS += -DSYSDEBUG
TARGET_ARCH ?= -msse3
endif endif
ifeq ($(MODE), aarch64) ifeq ($(MODE), aarch64)
ENABLE_FTRACE = 1 ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
CONFIG_CCFLAGS += -O2 $(BACKTRACES) CONFIG_CCFLAGS += -O2 $(BACKTRACES)
CONFIG_CPPFLAGS += -DSYSDEBUG CONFIG_CPPFLAGS += -DSYSDEBUG
endif endif
@ -37,15 +32,15 @@ endif
# #
ifeq ($(MODE), zero) ifeq ($(MODE), zero)
ENABLE_FTRACE = 1 ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
OVERRIDE_CFLAGS += -O0 OVERRIDE_CFLAGS += -O0
OVERRIDE_CXXFLAGS += -O0 OVERRIDE_CXXFLAGS += -O0
CONFIG_CPPFLAGS += -DSYSDEBUG CONFIG_CPPFLAGS += -DSYSDEBUG
endif endif
ifeq ($(MODE), aarch64-zero) ifeq ($(MODE), aarch64-zero)
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
OVERRIDE_CFLAGS += -O0 -fdce OVERRIDE_CFLAGS += -O0
OVERRIDE_CXXFLAGS += -O0 -fdce OVERRIDE_CXXFLAGS += -O0
CONFIG_CPPFLAGS += -DSYSDEBUG CONFIG_CPPFLAGS += -DSYSDEBUG
endif endif
@ -63,6 +58,7 @@ ENABLE_FTRACE = 1
CONFIG_CCFLAGS += $(BACKTRACES) -O CONFIG_CCFLAGS += $(BACKTRACES) -O
CONFIG_CPPFLAGS += -DSYSDEBUG -DDWARFLESS CONFIG_CPPFLAGS += -DSYSDEBUG -DDWARFLESS
CONFIG_LDFLAGS += -S CONFIG_LDFLAGS += -S
TARGET_ARCH ?= -msse3
endif endif
# Optimized Mode # Optimized Mode
@ -79,10 +75,10 @@ endif
# #
ifeq ($(MODE), opt) ifeq ($(MODE), opt)
ENABLE_FTRACE = 1 ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG
CONFIG_CCFLAGS += $(BACKTRACES) -O3 -fmerge-all-constants CONFIG_CCFLAGS += $(BACKTRACES) -O3 -fmerge-all-constants
CONFIG_TARGET_ARCH ?= -march=native TARGET_ARCH ?= -march=native
endif endif
# Optimized Linux Mode # Optimized Linux Mode
@ -96,24 +92,11 @@ endif
# - Turns off support for other operating systems # - Turns off support for other operating systems
# #
ifeq ($(MODE), optlinux) ifeq ($(MODE), optlinux)
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone
CONFIG_TARGET_ARCH ?= -march=native
endif
ifeq ($(MODE), x86_64-optlinux)
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone
CONFIG_TARGET_ARCH ?= -march=native
endif
ifeq ($(MODE), aarch64-optlinux)
CONFIG_OFLAGS ?= -g -ggdb
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1 CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
CONFIG_CCFLAGS += -O3 -fmerge-all-constants CONFIG_CCFLAGS += -O3 -fmerge-all-constants
CONFIG_COPTS += -mred-zone CONFIG_COPTS += -mred-zone
TARGET_ARCH ?= -march=native
endif endif
# Release Mode # Release Mode
@ -134,13 +117,36 @@ endif
ifeq ($(MODE), rel) ifeq ($(MODE), rel)
CONFIG_CPPFLAGS += -DNDEBUG -DDWARFLESS CONFIG_CPPFLAGS += -DNDEBUG -DDWARFLESS
CONFIG_CCFLAGS += $(BACKTRACES) -O2 CONFIG_CCFLAGS += $(BACKTRACES) -O2
TARGET_ARCH ?= -msse3
PYFLAGS += -O1 PYFLAGS += -O1
endif endif
# Asan Mode
#
# Safer binaries good for backend production serving.
#
# - `make MODE=asan`
# - Memory safety
# - Production worthy
# - Backtraces
# - Debuggability
# - Larger binaries
#
ifeq ($(MODE), asan)
ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g
CONFIG_CPPFLAGS += -D__SANITIZE_ADDRESS__
CONFIG_CCFLAGS += $(BACKTRACES) -O2 -DSYSDEBUG
CONFIG_COPTS += -fsanitize=address
TARGET_ARCH ?= -msse3
QUOTA ?= -C64 -L300
endif
# Debug Mode # Debug Mode
# #
# - `make MODE=dbg` # - `make MODE=dbg`
# - Backtraces # - Backtraces
# - Enables asan
# - Enables ubsan # - Enables ubsan
# - Stack canaries # - Stack canaries
# - No optimization # - No optimization
@ -148,33 +154,19 @@ endif
# #
ifeq ($(MODE), dbg) ifeq ($(MODE), dbg)
ENABLE_FTRACE = 1 ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
OVERRIDE_CFLAGS += -O0 CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_ADDRESS__ -D__SANITIZE_UNDEFINED__
OVERRIDE_CXXFLAGS += -O0 CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O0 -fno-inline
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable CONFIG_COPTS += -fsanitize=address -fsanitize=undefined
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG TARGET_ARCH ?= -msse3
CONFIG_COPTS += -fsanitize=undefined
OVERRIDE_CCFLAGS += -fno-pie
QUOTA ?= -C64 -L300
endif
ifeq ($(MODE), x86_64-dbg)
ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb
OVERRIDE_CFLAGS += -O0
OVERRIDE_CXXFLAGS += -O0
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
CONFIG_COPTS += -fsanitize=undefined
OVERRIDE_CCFLAGS += -fno-pie OVERRIDE_CCFLAGS += -fno-pie
QUOTA ?= -C64 -L300 QUOTA ?= -C64 -L300
endif endif
ifeq ($(MODE), aarch64-dbg) ifeq ($(MODE), aarch64-dbg)
ENABLE_FTRACE = 1 ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
OVERRIDE_CFLAGS += -O0 -fdce CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__
OVERRIDE_CXXFLAGS += -O0 -fdce CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O0 -fno-inline
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
CONFIG_COPTS += -fsanitize=undefined CONFIG_COPTS += -fsanitize=undefined
QUOTA ?= -C64 -L300 QUOTA ?= -C64 -L300
endif endif
@ -191,9 +183,10 @@ endif
# #
ifeq ($(MODE), sysv) ifeq ($(MODE), sysv)
ENABLE_FTRACE = 1 ENABLE_FTRACE = 1
CONFIG_OFLAGS ?= -g -ggdb CONFIG_OFLAGS ?= -g
CONFIG_CCFLAGS += $(BACKTRACES) -O2 CONFIG_CCFLAGS += $(BACKTRACES) -O2
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121 CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
TARGET_ARCH ?= -msse3
endif endif
# Tiny Mode # Tiny Mode
@ -223,25 +216,8 @@ CONFIG_CCFLAGS += \
-momit-leaf-frame-pointer \ -momit-leaf-frame-pointer \
-foptimize-sibling-calls \ -foptimize-sibling-calls \
-DDWARFLESS -DDWARFLESS
PYFLAGS += \ TARGET_ARCH ?= \
-O2 \ -msse3
-B
endif
ifeq ($(MODE), x86_64-tiny)
CONFIG_CPPFLAGS += \
-DTINY \
-DNDEBUG \
-DTRUSTWORTHY
CONFIG_CCFLAGS += \
-Os \
-fno-align-functions \
-fno-align-jumps \
-fno-align-labels \
-fno-align-loops \
-fschedule-insns2 \
-momit-leaf-frame-pointer \
-foptimize-sibling-calls \
-DDWARFLESS
PYFLAGS += \ PYFLAGS += \
-O2 \ -O2 \
-B -B
@ -293,6 +269,8 @@ CONFIG_CCFLAGS += \
-fno-align-jumps \ -fno-align-jumps \
-fno-align-labels \ -fno-align-labels \
-fno-align-loops -fno-align-loops
TARGET_ARCH ?= \
-msse3
endif endif
# Linux+BSD Tiny Mode # Linux+BSD Tiny Mode
@ -322,6 +300,8 @@ CONFIG_CCFLAGS += \
-fno-align-jumps \ -fno-align-jumps \
-fno-align-labels \ -fno-align-labels \
-fno-align-loops -fno-align-loops
TARGET_ARCH ?= \
-msse3
endif endif
# Unix Tiny Mode # Unix Tiny Mode
@ -350,6 +330,8 @@ CONFIG_CCFLAGS += \
-fno-align-jumps \ -fno-align-jumps \
-fno-align-labels \ -fno-align-labels \
-fno-align-loops -fno-align-loops
TARGET_ARCH ?= \
-msse3
endif endif
# Tiny Metallic Unix Mode # Tiny Metallic Unix Mode
@ -378,6 +360,8 @@ CONFIG_CCFLAGS += \
-fno-align-jumps \ -fno-align-jumps \
-fno-align-labels \ -fno-align-labels \
-fno-align-loops -fno-align-loops
TARGET_ARCH ?= \
-msse3
endif endif
# no x87 instructions mode # no x87 instructions mode
@ -399,6 +383,7 @@ ENABLE_FTRACE = 1
CONFIG_COPTS += -mlong-double-64 CONFIG_COPTS += -mlong-double-64
CONFIG_CCFLAGS += $(BACKTRACES) -O2 CONFIG_CCFLAGS += $(BACKTRACES) -O2
CONFIG_CPPFLAGS += -DSYSDEBUG -DNOX87 CONFIG_CPPFLAGS += -DSYSDEBUG -DNOX87
TARGET_ARCH ?= -msse3
endif endif
# LLVM Mode # LLVM Mode
@ -411,6 +396,7 @@ endif
# #
ifeq ($(MODE), llvm) ifeq ($(MODE), llvm)
.STRICT = 0 .STRICT = 0
TARGET_ARCH ?= -msse3
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O2 CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O2
AS = clang AS = clang
CC = clang CC = clang
@ -453,6 +439,7 @@ ifeq ($(MODE), ansi)
CONFIG_CFLAGS += -std=c11 CONFIG_CFLAGS += -std=c11
#CONFIG_CPPFLAGS += -ansi #CONFIG_CPPFLAGS += -ansi
CONFIG_CXXFLAGS += -std=c++11 CONFIG_CXXFLAGS += -std=c++11
TARGET_ARCH ?= -msse3
endif endif
ifneq ($(ENABLE_FTRACE),) ifneq ($(ENABLE_FTRACE),)
@ -510,5 +497,3 @@ ifeq ($(ARCH), aarch64)
CONFIG_CCFLAGS += -fpatchable-function-entry=7,6 CONFIG_CCFLAGS += -fpatchable-function-entry=7,6
endif endif
endif endif
TARGET_ARCH ?= $(CONFIG_TARGET_ARCH)

View file

@ -1,5 +1,5 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ #-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set et ft=make ts=8 sw=8 fenc=utf-8 :vi ──────────────────────┘ #───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
# #
# SYNOPSIS # SYNOPSIS
# #
@ -53,23 +53,111 @@
# OVERRIDE_FOO set ~/.cosmo.mk and target-specific (use rarely) # OVERRIDE_FOO set ~/.cosmo.mk and target-specific (use rarely)
# #
LC_ALL = C
SOURCE_DATE_EPOCH = 0
ARFLAGS = rcsD
ZFLAGS ?=
XARGS ?= xargs -P4 -rs8000
DOT ?= dot
CLANG = clang
FC = gfortran #/opt/cross9f/bin/x86_64-linux-musl-gfortran
TMPDIR = o/tmp
AR = build/bootstrap/ar.com
CP = build/bootstrap/cp.com
RM = build/bootstrap/rm.com -f
GZIP = build/bootstrap/gzip.com
ECHO = build/bootstrap/echo.com
CHMOD = build/bootstrap/chmod.com
TOUCH = build/bootstrap/touch.com
PKG = build/bootstrap/package.com
MKDEPS = build/bootstrap/mkdeps.com
ZIPOBJ = build/bootstrap/zipobj.com
ZIPCOPY = build/bootstrap/zipcopy.com
FIXUPOBJ = build/bootstrap/fixupobj.com
MKDIR = build/bootstrap/mkdir.com -p
COMPILE = build/bootstrap/compile.com -V9 -P4096 $(QUOTA)
COMMA := ,
PWD := $(shell build/bootstrap/pwd.com)
IGNORE := $(shell $(ECHO) -2 ♥cosmo)
IGNORE := $(shell $(MKDIR) $(TMPDIR))
ifneq ($(findstring aarch64,$(MODE)),)
ARCH = aarch64
VM = o/third_party/qemu/qemu-aarch64
HOSTS ?= pi silicon
else
ARCH = x86_64
HOSTS ?= freebsd openbsd openbsd73 netbsd rhel7 rhel5 xnu win10
endif
ifeq ($(PREFIX),)
ifeq ($(USE_SYSTEM_TOOLCHAIN),)
ifneq ("$(wildcard o/third_party/gcc/bin/x86_64-pc-linux-gnu-*)","")
PREFIX = o/third_party/gcc/bin/x86_64-pc-linux-gnu-
else
IGNORE := $(shell build/bootstrap/unbundle.com)
PREFIX = o/third_party/gcc/bin/x86_64-linux-musl-
endif
ifeq ($(ARCH), aarch64)
PREFIX = o/third_party/gcc/bin/aarch64-linux-musl-
endif
endif
endif
AS = $(PREFIX)as
CC = $(PREFIX)gcc
CXX = $(PREFIX)g++
CXXFILT = $(PREFIX)c++filt
LD = $(PREFIX)ld.bfd
NM = $(PREFIX)nm
GCC = $(PREFIX)gcc
STRIP = $(PREFIX)strip
OBJCOPY = $(PREFIX)objcopy
OBJDUMP = $(PREFIX)objdump
ifneq ($(wildcard $(PWD)/$(PREFIX)addr2line), )
ADDR2LINE = $(PWD)/$(PREFIX)addr2line
else
ADDR2LINE = $(PREFIX)addr2line
endif
export ADDR2LINE
export LC_ALL
export MKDIR
export MODE
export SOURCE_DATE_EPOCH
export TMPDIR
ifeq ($(LANDLOCKMAKE_VERSION),) ifeq ($(LANDLOCKMAKE_VERSION),)
TMPSAFE = $(join $(TMPDIR)/,$(subst /,_,$@)).tmp TMPSAFE = $(join $(TMPDIR),$(subst /,_,$@)).tmp
else else
TMPSAFE = $(TMPDIR)/ TMPSAFE = $(TMPDIR)/
endif endif
ifeq ($(ARCH), aarch64)
IMAGE_BASE_VIRTUAL ?= 0x010000000000
else
IMAGE_BASE_VIRTUAL ?= 0x400000
endif
BACKTRACES = \ BACKTRACES = \
-fno-schedule-insns2 \
-fno-optimize-sibling-calls \ -fno-optimize-sibling-calls \
-mno-omit-leaf-frame-pointer -mno-omit-leaf-frame-pointer
ifneq ($(ARCH), aarch64)
BACKTRACES += -fno-schedule-insns2
endif
SANITIZER = \
-fsanitize=address
NO_MAGIC = \ NO_MAGIC = \
-ffreestanding \
-fno-stack-protector \ -fno-stack-protector \
-fwrapv \ -fwrapv \
-fno-sanitize=all \ -fno-sanitize=all
-fpatchable-function-entry=0,0
OLD_CODE = \ OLD_CODE = \
-fno-strict-aliasing \ -fno-strict-aliasing \
@ -87,18 +175,18 @@ DEFAULT_CCFLAGS += \
-frecord-gcc-switches -frecord-gcc-switches
DEFAULT_COPTS ?= \ DEFAULT_COPTS ?= \
-fno-math-errno \
-fno-ident \ -fno-ident \
-fno-common \ -fno-common \
-fno-gnu-unique \ -fno-gnu-unique \
-fstrict-aliasing \ -fstrict-aliasing \
-fstrict-overflow \ -fstrict-overflow \
-fno-semantic-interposition -fno-semantic-interposition \
-fno-dwarf2-cfi-asm \
-fno-unwind-tables \
-fno-asynchronous-unwind-tables
ifeq ($(ARCH), x86_64) ifeq ($(ARCH), x86_64)
# Microsoft says "[a]ny memory below the stack beyond the red zone
# [note: Windows defines the x64 red zone size as 0] is considered
# volatile and may be modified by the operating system at any time."
# https://devblogs.microsoft.com/oldnewthing/20190111-00/?p=100685
DEFAULT_COPTS += \ DEFAULT_COPTS += \
-mno-red-zone \ -mno-red-zone \
-mno-tls-direct-seg-refs -mno-tls-direct-seg-refs
@ -115,7 +203,7 @@ ifeq ($(ARCH), aarch64)
DEFAULT_COPTS += \ DEFAULT_COPTS += \
-ffixed-x18 \ -ffixed-x18 \
-ffixed-x28 \ -ffixed-x28 \
-fsigned-char -mno-outline-atomics
endif endif
MATHEMATICAL = \ MATHEMATICAL = \
@ -123,24 +211,23 @@ MATHEMATICAL = \
-fwrapv -fwrapv
DEFAULT_CPPFLAGS += \ DEFAULT_CPPFLAGS += \
-D_COSMO_SOURCE \ -DCOSMO \
-DMODE='"$(MODE)"' \ -DMODE='"$(MODE)"' \
-Wno-prio-ctor-dtor \ -DIMAGE_BASE_VIRTUAL=$(IMAGE_BASE_VIRTUAL) \
-Wno-unknown-pragmas \
-nostdinc \ -nostdinc \
-iquote. \ -iquote .
-isystem libc/isystem
DEFAULT_CFLAGS = \ DEFAULT_CFLAGS = \
-std=gnu23 -std=gnu2x
DEFAULT_CXXFLAGS = \ DEFAULT_CXXFLAGS = \
-std=gnu++23 \ -fno-rtti \
-fno-exceptions \
-fuse-cxa-atexit \ -fuse-cxa-atexit \
-fno-threadsafe-statics \
-Wno-int-in-bool-context \ -Wno-int-in-bool-context \
-Wno-narrowing \ -Wno-narrowing \
-Wno-literal-suffix \ -Wno-literal-suffix
-isystem third_party/libcxx
DEFAULT_ASFLAGS = \ DEFAULT_ASFLAGS = \
-W \ -W \
@ -152,7 +239,6 @@ DEFAULT_LDFLAGS = \
-nostdlib \ -nostdlib \
-znorelro \ -znorelro \
--gc-sections \ --gc-sections \
-z noexecstack \
--build-id=none \ --build-id=none \
--no-dynamic-linker --no-dynamic-linker
@ -166,7 +252,7 @@ DEFAULT_LDFLAGS += \
-znorelro -znorelro
else else
DEFAULT_LDFLAGS += \ DEFAULT_LDFLAGS += \
-zmax-page-size=0x4000 \ -zmax-page-size=0x10000 \
-zcommon-page-size=0x1000 -zcommon-page-size=0x1000
endif endif
@ -247,22 +333,30 @@ LD.libs = \
$(LIBS) $(LIBS)
COMPILE.c.flags = $(cc.flags) $(copt.flags) $(cpp.flags) $(c.flags) COMPILE.c.flags = $(cc.flags) $(copt.flags) $(cpp.flags) $(c.flags)
COMPILE.cxx.flags = $(cc.flags) $(copt.flags) $(cxx.flags) $(cpp.flags) COMPILE.cxx.flags = $(cc.flags) $(copt.flags) $(cpp.flags) $(cxx.flags)
COMPILE.f.flags = $(cc.flags) $(copt.flags) $(f.flags)
COMPILE.F.flags = $(cc.flags) $(copt.flags) $(cpp.flags) $(f.flags)
COMPILE.i.flags = $(cc.flags) $(copt.flags) $(c.flags) COMPILE.i.flags = $(cc.flags) $(copt.flags) $(c.flags)
COMPILE.ii.flags = $(cc.flags) $(copt.flags) $(cxx.flags) COMPILE.ii.flags = $(cc.flags) $(copt.flags) $(cxx.flags)
LINK.flags = $(DEFAULT_LDFLAGS) $(CONFIG_LDFLAGS) $(LDFLAGS) LINK.flags = $(DEFAULT_LDFLAGS) $(CONFIG_LDFLAGS) $(LDFLAGS)
OBJECTIFY.c.flags = $(cc.flags) $(o.flags) $(S.flags) $(cpp.flags) $(copt.flags) $(c.flags) OBJECTIFY.c.flags = $(cc.flags) $(o.flags) $(S.flags) $(cpp.flags) $(copt.flags) $(c.flags)
OBJECTIFY.cxx.flags = $(cc.flags) $(o.flags) $(S.flags) $(cxx.flags) $(cpp.flags) $(copt.flags) OBJECTIFY.cxx.flags = $(cc.flags) $(o.flags) $(S.flags) $(cpp.flags) $(copt.flags) $(cxx.flags)
OBJECTIFY.s.flags = $(ASONLYFLAGS) $(s.flags) OBJECTIFY.s.flags = $(ASONLYFLAGS) $(s.flags)
OBJECTIFY.S.flags = $(cc.flags) $(o.flags) $(S.flags) $(cpp.flags) OBJECTIFY.S.flags = $(cc.flags) $(o.flags) $(S.flags) $(cpp.flags)
OBJECTIFY.f.flags = $(cc.flags) $(o.flags) $(S.flags) $(f.flags)
OBJECTIFY.F.flags = $(cc.flags) $(o.flags) $(S.flags) $(cpp.flags) $(copt.flags) $(f.flags)
PREPROCESS.flags = -E $(copt.flags) $(cc.flags) $(cpp.flags) PREPROCESS.flags = -E $(copt.flags) $(cc.flags) $(cpp.flags)
PREPROCESS.lds.flags = -D__LINKER__ $(filter-out -g%,$(PREPROCESS.flags)) -P -xc PREPROCESS.lds.flags = -D__LINKER__ $(filter-out -g%,$(PREPROCESS.flags)) -P -xc
COMPILE.c = $(CC) -S $(COMPILE.c.flags) COMPILE.c = $(CC) -S $(COMPILE.c.flags)
COMPILE.cxx = $(CXX) -S $(COMPILE.cxx.flags) COMPILE.cxx = $(CXX) -S $(COMPILE.cxx.flags)
COMPILE.i = $(CC) -S $(COMPILE.i.flags) COMPILE.i = $(CC) -S $(COMPILE.i.flags)
COMPILE.f = $(FC) -S $(COMPILE.f.flags)
COMPILE.F = $(FC) -S $(COMPILE.F.flags)
OBJECTIFY.s = $(AS) $(OBJECTIFY.s.flags) OBJECTIFY.s = $(AS) $(OBJECTIFY.s.flags)
OBJECTIFY.S = $(CC) $(OBJECTIFY.S.flags) -c OBJECTIFY.S = $(CC) $(OBJECTIFY.S.flags) -c
OBJECTIFY.f = $(FC) $(OBJECTIFY.f.flags) -c
OBJECTIFY.F = $(FC) $(OBJECTIFY.F.flags) -c
OBJECTIFY.c = $(CC) $(OBJECTIFY.c.flags) -c OBJECTIFY.c = $(CC) $(OBJECTIFY.c.flags) -c
OBJECTIFY.cxx = $(CXX) $(OBJECTIFY.cxx.flags) -c OBJECTIFY.cxx = $(CXX) $(OBJECTIFY.cxx.flags) -c
PREPROCESS = $(CC) $(PREPROCESS.flags) PREPROCESS = $(CC) $(PREPROCESS.flags)
@ -271,6 +365,7 @@ LINK = $(LD) $(LINK.flags)
ELF = o/libc/elf/elf.lds ELF = o/libc/elf/elf.lds
ELFLINK = $(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) && $(COMPILE) -AFIXUP.ape -T$@ $(FIXUPOBJ) $@ ELFLINK = $(COMPILE) -ALINK.elf $(LINK) $(LINKARGS) $(OUTPUT_OPTION) && $(COMPILE) -AFIXUP.ape -T$@ $(FIXUPOBJ) $@
LINKARGS = $(patsubst %.lds,-T %.lds,$(call uniqr,$(LD.libs) $(filter-out %.pkg,$^))) LINKARGS = $(patsubst %.lds,-T %.lds,$(call uniqr,$(LD.libs) $(filter-out %.pkg,$^)))
LOLSAN = build/lolsan -b $(IMAGE_BASE_VIRTUAL)
# The compiler won't generate %xmm code for sources extensioned .greg.c, # The compiler won't generate %xmm code for sources extensioned .greg.c,
# which is needed for C modules wanting to run at the executive level or # which is needed for C modules wanting to run at the executive level or
@ -288,6 +383,68 @@ OBJECTIFY.greg.c = \
-fwrapv \ -fwrapv \
-c -c
OBJECTIFY.ansi.c = $(CC) $(OBJECTIFY.c.flags) -ansi -Wextra -Werror -pedantic-errors -c
OBJECTIFY.c99.c = $(CC) $(OBJECTIFY.c.flags) -std=c99 -Wextra -Werror -pedantic-errors -c
OBJECTIFY.c11.c = $(CC) $(OBJECTIFY.c.flags) -std=c11 -Wextra -Werror -pedantic-errors -c
OBJECTIFY.c2x.c = $(CC) $(OBJECTIFY.c.flags) -std=c2x -Wextra -Werror -pedantic-errors -c
OBJECTIFY.real.c = \
$(GCC) \
-x-no-pg \
$(OBJECTIFY.c.flags) \
-wrapper build/realify.sh \
-D__REAL_MODE__ \
-ffixed-r8 \
-ffixed-r9 \
-ffixed-r10 \
-ffixed-r11 \
-ffixed-r12 \
-ffixed-r13 \
-ffixed-r14 \
-ffixed-r15 \
-mno-red-zone \
-fcall-used-rbx \
-fno-jump-tables \
-fno-shrink-wrap \
-fno-schedule-insns2 \
-flive-range-shrinkage \
-fno-omit-frame-pointer \
-momit-leaf-frame-pointer \
-mpreferred-stack-boundary=3 \
-fno-delete-null-pointer-checks \
-c
OBJECTIFY.ncabi.c = \
$(GCC) \
$(OBJECTIFY.c.flags) \
-mno-sse \
-mfpmath=387 \
-fno-stack-protector \
-fno-instrument-functions \
-fno-optimize-sibling-calls \
-fno-sanitize=all \
-fcall-saved-rcx \
-fcall-saved-rdx \
-fcall-saved-rdi \
-fcall-saved-rsi \
-fcall-saved-r8 \
-fcall-saved-r9 \
-fcall-saved-r10 \
-fcall-saved-r11 \
-c \
-xc
OBJECTIFY.initabi.c = \
$(GCC) \
$(OBJECTIFY.c.flags) \
-fno-stack-protector \
-fno-instrument-functions \
-fno-optimize-sibling-calls \
-fno-sanitize=all \
-fcall-saved-rdi \
-fcall-saved-rsi \
-c
TAGSFLAGS = \ TAGSFLAGS = \
-e \ -e \
-a \ -a \

View file

@ -1,106 +0,0 @@
#!/bin/sh
# cosmocc downloader script
# https://justine.lol/cosmo3/#install
# https://github.com/jart/cosmopolitan/blob/master/tool/cosmocc/README.md
# collect arguments
OUTPUT_DIR=${1:?OUTPUT_DIR}
COSMOCC_VERSION=${2:?COSMOCC_VERSION}
COSMOCC_SHA256SUM=${3:?COSMOCC_SHA256SUM}
URL1="https://github.com/jart/cosmopolitan/releases/download/${COSMOCC_VERSION}/cosmocc-${COSMOCC_VERSION}.zip"
URL2="https://cosmo.zip/pub/cosmocc/cosmocc-${COSMOCC_VERSION}.zip"
# helper function
abort() {
printf '%s\n' "download terminated." >&2
exit 1
}
# exit if already downloaded
# we need it because directory timestamps work wierdly
OUTPUT_DIR=${OUTPUT_DIR%/}
if [ -d "${OUTPUT_DIR}" ]; then
exit 0
fi
# find commands we need to securely download cosmocc
if ! UNZIP=$(command -v unzip 2>/dev/null); then
printf '%s\n' "$0: fatal error: you need the unzip command" >&2
printf '%s\n' "please download https://cosmo.zip/pub/cosmos/bin/unzip and put it on the system path" >&2
abort
fi
if command -v sha256sum >/dev/null 2>&1; then
# can use system sha256sum
true
elif command -v shasum >/dev/null 2>&1; then
sha256sum() {
shasum -a 256 "$@"
}
else
if [ ! -f build/sha256sum.c ]; then
printf '%s\n' "$0: fatal error: you need to install sha256sum" >&2
printf '%s\n' "please download https://cosmo.zip/pub/cosmos/bin/sha256sum and put it on the system path" >&2
abort
fi
if ! SHA256SUM=$(command -v "$PWD/o/build/sha256sum" 2>/dev/null); then
if ! CC=$(command -v "$CC" 2>/dev/null); then
if ! CC=$(command -v cc 2>/dev/null); then
if ! CC=$(command -v cosmocc 2>/dev/null); then
printf '%s\n' "$0: fatal error: you need to install either sha256sum, cc, or cosmocc" >&2
printf '%s\n' "please download https://cosmo.zip/pub/cosmos/bin/sha256sum and put it on the system path" >&2
abort
fi
fi
fi
mkdir -p o/build || abort
SHA256SUM="$PWD/o/build/sha256sum"
printf '%s\n' "${CC} -w -O2 -o ${SHA256SUM} build/sha256sum.c" >&2
"${CC}" -w -O2 -o "${SHA256SUM}.$$" build/sha256sum.c || abort
mv -f "${SHA256SUM}.$$" "${SHA256SUM}" || abort
fi
sha256sum() {
"${SHA256SUM}" "$@"
}
fi
if WGET=$(command -v wget 2>/dev/null); then
DOWNLOAD=$WGET
DOWNLOAD_ARGS=-O
elif CURL=$(command -v curl 2>/dev/null); then
DOWNLOAD=$CURL
DOWNLOAD_ARGS=-fLo
else
printf '%s\n' "$0: fatal error: you need to install either wget or curl" >&2
printf '%s\n' "please download https://cosmo.zip/pub/cosmos/bin/wget and put it on the system path" >&2
abort
fi
# create temporary output directory
OLDPWD=$PWD
OUTPUT_TMP="${OUTPUT_DIR}.tmp.$$/"
mkdir -p "${OUTPUT_TMP}" || abort
cd "${OUTPUT_TMP}"
die() {
cd "${OLDPWD}"
rm -rf "${OUTPUT_TMP}"
abort
}
# download cosmocc toolchain
# multiple urls avoids outages and national firewalls
if ! "${DOWNLOAD}" ${DOWNLOAD_ARGS} cosmocc.zip "${URL1}"; then
rm -f cosmocc.zip
"${DOWNLOAD}" ${DOWNLOAD_ARGS} cosmocc.zip "${URL2}" || die
fi
printf '%s\n' "${COSMOCC_SHA256SUM} *cosmocc.zip" >cosmocc.zip.sha256sum
sha256sum -c cosmocc.zip.sha256sum || die
"${UNZIP}" cosmocc.zip || die
rm -f cosmocc.zip cosmocc.zip.sha256sum
# commit output directory
cd "${OLDPWD}" || die
mv "${OUTPUT_TMP}" "${OUTPUT_DIR}" || die
# update current symlink
BASE=$(basename "${OUTPUT_DIR}")
DIR=$(dirname "${OUTPUT_DIR}")
ln -sfn "$BASE" "$DIR/current"

View file

@ -1,5 +1,5 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ #-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set et ft=make ts=8 sw=8 fenc=utf-8 :vi ──────────────────────┘ #───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
# #
# SYNOPSIS # SYNOPSIS
# #

View file

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐ #-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐
#── vi: set et ft=sh ts=2 sts=2 fenc=utf-8 :vi ─────────────┘ #───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘
# #
# OVERVIEW # OVERVIEW
# #
@ -55,7 +55,7 @@ set -- --regex-c='/_Atomic(\([^)]*\))/\1/b' "$@"
set -- --regex-c='/^\(\(hidden\|extern\|const\) \)*[_[:alpha:]][_[:alnum:]]*[ *][ *]*\([_[:alpha:]][_[:alnum:]]*[ *][ *]*\)*\([_[:alpha:]][_$[:alnum:]]*\)/\4/b' "$@" set -- --regex-c='/^\(\(hidden\|extern\|const\) \)*[_[:alpha:]][_[:alnum:]]*[ *][ *]*\([_[:alpha:]][_[:alnum:]]*[ *][ *]*\)*\([_[:alpha:]][_$[:alnum:]]*\)/\4/b' "$@"
# ctags doesn't understand function prototypes, e.g. # ctags doesn't understand function prototypes, e.g.
# bool isheap(void *p) dontthrow dontcallback; # bool isheap(void *p) dontthrow nocallback;
set -- --regex-c='/^[_[:alpha:]][_[:alnum:]]*[ *][ *]*\([_[:alpha:]][_[:alnum:]]*[ *][ *]*\)*\([_[:alpha:]][_$[:alnum:]]*\)(.*/\2/b' "$@" set -- --regex-c='/^[_[:alpha:]][_[:alnum:]]*[ *][ *]*\([_[:alpha:]][_[:alnum:]]*[ *][ *]*\)*\([_[:alpha:]][_$[:alnum:]]*\)(.*/\2/b' "$@"
# ctags doesn't understand function pointers, e.g. # ctags doesn't understand function pointers, e.g.
@ -66,7 +66,7 @@ set -- --regex-c='/^extern [^(]*(\*const \([^)]*\))(/\1/b' "$@"
# struct WorstSoftwareEver; # struct WorstSoftwareEver;
set -- --regex-c='/^struct.*;$/uehocruehcroue/b' "$@" set -- --regex-c='/^struct.*;$/uehocruehcroue/b' "$@"
build/run $TAGS \ exec $TAGS \
-e \ -e \
--langmap=c:.c.h \ --langmap=c:.c.h \
--exclude=libc/nt/struct/imagefileheader.internal.h \ --exclude=libc/nt/struct/imagefileheader.internal.h \

View file

@ -1,29 +1,6 @@
#!/bin/sh #!/bin/sh
COSMO=${COSMO:-/opt/cosmo}
if [ -n "$OBJDUMP" ]; then
exec "$OBJDUMP" "$@"
fi
find_objdump() {
if [ -x .cosmocc/3.9.2/bin/$1-linux-cosmo-objdump ]; then
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump
elif [ -x .cosmocc/3.9.2/bin/$1-linux-musl-objdump ]; then
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-musl-objdump
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump"
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump" ]; then
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump"
else
echo "error: toolchain not found (try running 'cosmocc --update' or 'make' in the cosmo monorepo)" >&2
exit 1
fi
}
if printf '%s\n' "$*" | grep aarch64 >/dev/null 2>&1; then if printf '%s\n' "$*" | grep aarch64 >/dev/null 2>&1; then
find_objdump aarch64 exec o/third_party/gcc/bin/aarch64-linux-musl-objdump "$@"
exec "$OBJDUMP" "$@"
else else
find_objdump x86_64 exec o/third_party/gcc/bin/x86_64-linux-musl-objdump "$@"
exec "$OBJDUMP" "$@"
fi fi

View file

@ -1,5 +1,5 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ #-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set et ft=make ts=8 sw=8 fenc=utf-8 :vi ──────────────────────┘ #───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
# #
# SYNOPSIS # SYNOPSIS
# #
@ -22,14 +22,10 @@
# - tool/build/runit.c # - tool/build/runit.c
# - tool/build/runitd.c # - tool/build/runitd.c
.PRECIOUS: o/$(MODE)/%.ok .PRECIOUS: o/$(MODE)/%.com.ok
o/$(MODE)/%.ok: private .PLEDGE = stdio rpath wpath cpath proc fattr inet dns o/$(MODE)/%.com.ok: private .PLEDGE = stdio rpath wpath cpath proc fattr inet
o/$(MODE)/%.ok: private .UNVEIL += r:/etc/resolv.conf o/$(MODE)/%.com.ok: \
o/$(MODE)/%.ok: \ o//tool/build/runit.com \
o/$(MODE)/tool/build/runit \ o//tool/build/runitd.com \
o/$(MODE)/tool/build/runitd \ o/$(MODE)/%.com
o/$(MODE)/%
@$(COMPILE) -wATEST -tT$@ $^ $(HOSTS) @$(COMPILE) -wATEST -tT$@ $^ $(HOSTS)
.PHONY:
o/tiny/tool/build/runit:

View file

@ -1,5 +1,5 @@
#-*-mode:sed;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ #-*-mode:sed;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: et ft=sed ts=8 sw=8 fenc=utf-8 :vi ────────────────┘ #───vi: et ft=sed ts=8 tw=8 fenc=utf-8 :vi─────────────────┘
# #
# SYNOPSIS # SYNOPSIS
# #

View file

@ -1,5 +1,5 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ #-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set et ft=make ts=8 sw=8 fenc=utf-8 :vi ──────────────────────┘ #───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
# #
# SYNOPSIS # SYNOPSIS
# #
@ -23,8 +23,50 @@ else
MAKE_OBJCOPY = $(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S $< $@ && $(MAKE_ZIPCOPY) MAKE_OBJCOPY = $(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S $< $@ && $(MAKE_ZIPCOPY)
endif endif
o/$(MODE)/%: o/$(MODE)/%.dbg o/%.lds: %.lds ; @$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $<
@$(MAKE_OBJCOPY) o/%.inc: %.h ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) -D__ASSEMBLER__ -P $<
o/$(MODE)/%: o/$(MODE)/%.dbg ; @$(MAKE_OBJCOPY)
o/$(MODE)/%.o: %.f ; @$(COMPILE) -AOBJECTIFY.f $(OBJECTIFY.f) $(OUTPUT_OPTION) $<
o/$(MODE)/%.o: %.F ; @$(COMPILE) -AOBJECTIFY.F $(OBJECTIFY.F) $(OUTPUT_OPTION) $<
o/$(MODE)/%.h: %.c ; @$(COMPILE) -AAMALGAMATE $(PREPROCESS) $(OUTPUT_OPTION) -fdirectives-only -P $<
o/$(MODE)/%.h: o/$(MODE)/%.c ; @$(COMPILE) -AAMALGAMATE $(PREPROCESS) $(OUTPUT_OPTION) -fdirectives-only -P $<
o/$(MODE)/%.o: %.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $<
o/$(MODE)/%.lds: %.lds ; @$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $<
o/$(MODE)/%.initabi.o: %.initabi.c ; @$(COMPILE) -AOBJECTIFY.init $(OBJECTIFY.initabi.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.ncabi.o: %.ncabi.c ; @$(COMPILE) -AOBJECTIFY.nc $(OBJECTIFY.ncabi.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.real.o: %.c ; @$(COMPILE) -AOBJECTIFY.real $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
ifneq ($(ARCH), aarch64)
o/%.o: %.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $<
o/%.o: o/%.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $<
o/%.s: %.S ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) $<
o/%.s: o/%.S ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) $<
o/%.o: %.S ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) $<
o/%.o: o/%.S ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) $<
o/$(MODE)/%.o: %.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $<
o/$(MODE)/%.o: o/$(MODE)/%.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $<
o/$(MODE)/%.o: %.S ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) $<
o/$(MODE)/%.o: o/$(MODE)/%.S ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) $<
else
o/%.o: %.s libc/empty.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
o/%.o: o/%.s libc/empty.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
o/%.s: %.S libc/empty.s ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) libc/empty.s
o/%.s: o/%.S libc/empty.s ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) libc/empty.s
o/%.o: %.S libc/empty.s ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
o/%.o: o/%.S libc/empty.s ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
o/$(MODE)/%.o: %.s libc/empty.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
o/$(MODE)/%.o: o/$(MODE)/%.s libc/empty.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
o/$(MODE)/%.o: %.S libc/empty.s ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
o/$(MODE)/%.o: o/$(MODE)/%.S libc/empty.s ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
endif
o/%.o: %.cc
@$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $<
@$(COMPILE) -AFIXUPOBJ -wT$@ $(FIXUPOBJ) $@
o/%.o: o/%.cc
@$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $<
@$(COMPILE) -AFIXUPOBJ -wT$@ $(FIXUPOBJ) $@
o/$(MODE)/%.o: %.c o/$(MODE)/%.o: %.c
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) $(OUTPUT_OPTION) $< @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) $(OUTPUT_OPTION) $<
@ -34,30 +76,13 @@ o/$(MODE)/%.o: o/$(MODE)/%.c
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) $(OUTPUT_OPTION) $< @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) $(OUTPUT_OPTION) $<
@$(COMPILE) -AFIXUPOBJ -wT$@ $(FIXUPOBJ) $@ @$(COMPILE) -AFIXUPOBJ -wT$@ $(FIXUPOBJ) $@
o/$(MODE)/%.h: %.c o/$(MODE)/%.o: o/$(MODE)/%.cc
@$(COMPILE) -AAMALGAMATE $(PREPROCESS) $(OUTPUT_OPTION) -fdirectives-only -P $<
o/$(MODE)/%.o: %.cc
@$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $< @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $<
@$(COMPILE) -AFIXUPOBJ -wT$@ $(FIXUPOBJ) $@ @$(COMPILE) -AFIXUPOBJ -wT$@ $(FIXUPOBJ) $@
o/$(MODE)/%.o: %.cpp o/%.greg.o: %.greg.c
@$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) $(OUTPUT_OPTION) $< @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
@$(COMPILE) -AFIXUPOBJ -wT$@ $(FIXUPOBJ) $@
o/$(MODE)/%.lds: %.lds
@$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $<
ifneq ($(ARCH), aarch64)
o/$(MODE)/%.o: %.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) $<
o/$(MODE)/%.o: o/$(MODE)/%.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) $<
else
o/$(MODE)/%.o: %.S libc/empty.s
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
o/$(MODE)/%.o: o/$(MODE)/%.S libc/empty.s
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.s) $(OUTPUT_OPTION) libc/empty.s
endif
o/$(MODE)/%.o: %.greg.c o/$(MODE)/%.o: %.greg.c
@$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $< @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
@ -79,17 +104,17 @@ o/$(MODE)/%.pkg:
$(file >$(TMPSAFE).args,$(filter %.o,$^)) $(file >$(TMPSAFE).args,$(filter %.o,$^))
@$(COMPILE) -APACKAGE -wT$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) @$(TMPSAFE).args @$(COMPILE) -APACKAGE -wT$@ $(PKG) $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) @$(TMPSAFE).args
o/$(MODE)/%.o: %.py o/$(MODE)/third_party/python/pyobj o/$(MODE)/%.o: %.py o/$(MODE)/third_party/python/pyobj.com $(VM)
@$(COMPILE) -wAPYOBJ o/$(MODE)/third_party/python/pyobj $(PYFLAGS) -o $@ $< @$(COMPILE) -wAPYOBJ $(VM) o/$(MODE)/third_party/python/pyobj.com $(PYFLAGS) -o $@ $<
o/$(MODE)/%.pyc: %.py o/$(MODE)/third_party/python/pycomp o/$(MODE)/%.pyc: %.py o/$(MODE)/third_party/python/pycomp.com $(VM)
@$(COMPILE) -wAPYCOMP o/$(MODE)/third_party/python/pycomp $(PYCFLAGS) -o $@ $< @$(COMPILE) -wAPYCOMP $(VM) o/$(MODE)/third_party/python/pycomp.com $(PYCFLAGS) -o $@ $<
o/$(MODE)/%.lua: %.lua o/$(MODE)/third_party/lua/luac o/$(MODE)/%.lua: %.lua o/$(MODE)/third_party/lua/luac.com $(VM)
@$(COMPILE) -wALUAC o/$(MODE)/third_party/lua/luac -s -o $@ $< @$(COMPILE) -wALUAC $(VM) o/$(MODE)/third_party/lua/luac.com -s -o $@ $<
o/$(MODE)/%.lua.runs: %.lua o/$(MODE)/tool/net/redbean o/$(MODE)/%.lua.runs: %.lua o/$(MODE)/tool/net/redbean.com $(VM)
@$(COMPILE) -wALUA -tT$@ o/$(MODE)/tool/net/redbean $(LUAFLAGS) -i $< @$(COMPILE) -wALUA -tT$@ $(VM) o/$(MODE)/tool/net/redbean.com $(LUAFLAGS) -i $<
################################################################################ ################################################################################
# LOCAL UNIT TESTS # LOCAL UNIT TESTS
@ -108,34 +133,34 @@ o/$(MODE)/%.lua.runs: %.lua o/$(MODE)/tool/net/redbean
# #
# You could then run a command like: # You could then run a command like:
# #
# make -j8 o//test/libc/calls/openbsd_test.runs # make -j8 o//test/libc/calls/openbsd_test.com.runs
# #
# You need PROT_EXEC permission since ftrace morphs the binary. It's # You need PROT_EXEC permission since ftrace morphs the binary. It's
# also worth mentioning that the pledge command can simulate what # also worth mentioning that the pledge.com command can simulate what
# Landlock Make does: # Landlock Make does:
# #
# o//tool/build/pledge \ # o//tool/build/pledge.com \
# -v. -p 'stdio rpath wpath cpath tty prot_exec' \ # -v. -p 'stdio rpath wpath cpath tty prot_exec' \
# o//test/libc/calls/openbsd_test \ # o//test/libc/calls/openbsd_test.com \
# ----ftrace # ----ftrace
# #
# This is useful in the event a test binary should run by itself, but # This is useful in the event a test binary should run by itself, but
# fails to run beneath Landlock Make. It's also useful sometimes to # fails to run beneath Landlock Make. It's also useful sometimes to
# override the verbosity when running tests: # override the verbosity when running tests:
# #
# make V=5 TESTARGS=-b o//test/libc/calls/openbsd_test.runs # make V=5 TESTARGS=-b o//test/libc/calls/openbsd_test.com.runs
# #
# This way, if for some reason a test should fail but calls exit(0), # This way, if for some reason a test should fail but calls exit(0),
# then the stdout/stderr output, which would normally be suppressed, # then the stdout/stderr output, which would normally be suppressed,
# will actually be displayed. # will actually be displayed.
o/$(MODE)/%.runs: o/$(MODE)/% o/$(MODE)/%.runs: o/$(MODE)/% $(VM)
@$(COMPILE) -ACHECK -wtT$@ $< $(TESTARGS) @$(COMPILE) -ACHECK -wtT$@ $(VM) $< $(TESTARGS)
################################################################################ ################################################################################
# ELF ZIP FILES # ELF ZIP FILES
# #
# zipobj lets us do fast incremental linking of compressed data. # zipobj.com lets us do fast incremental linking of compressed data.
# it's nice because if we link a hundred binaries that use the time zone # it's nice because if we link a hundred binaries that use the time zone
# database, then that database only needs to be DEFLATE'd once. # database, then that database only needs to be DEFLATE'd once.
@ -183,6 +208,25 @@ o/$(MODE)/%.okk: private .UNSANDBOXED = 1
o/$(MODE)/%.okk: % o/$(MODE)/%.okk: %
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $< @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
################################################################################
# EXECUTABLE HELPERS
MAKE_SYMTAB_CREATE = \
$(COMPILE) -wASYMTAB \
$(VM) \
o/$(MODE)/tool/build/symtab.com \
-o $(TMPSAFE)/.symtab \
$<
MAKE_SYMTAB_ZIP = \
$(COMPILE) -AZIP -T$@ \
$(VM) \
o/$(MODE)/third_party/zip/zip.com \
-b$(TMPDIR) \
-9qj \
$@ \
$(TMPSAFE)/.symtab
################################################################################ ################################################################################
# EMACS ASSEMBLY GENERATION # EMACS ASSEMBLY GENERATION

View file

@ -1,8 +1,9 @@
#!/bin/sh #!/bin/sh
UNAMEM=$(uname -m) if printf '%s\n' "$*" | grep aarch64 >/dev/null 2>&1; then
UNAMES=$(uname -s) if [ ! -f o/third_party/qemu/qemu-aarch64 ]; then
if [ x"$UNAMES" = x"Darwin" ] && [ x"$UNAMEM" = x"arm64" ]; then make -j8 o/third_party/qemu/qemu-aarch64
exec ape "$@" fi
exec o/third_party/qemu/qemu-aarch64 "$@"
else else
exec rusage "$@" exec "$@"
fi fi

View file

@ -31,7 +31,7 @@ if [ ! -f /proc/sys/fs/binfmt_misc/status ]; then
exit 0 exit 0
fi fi
if ! build/bootstrap/echo -n; then if ! build/bootstrap/echo.com -n; then
cat <<'EOF' >&2 cat <<'EOF' >&2
ERROR ERROR

View file

@ -1,455 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// this file should not have dependencies, because everything will be
// re-downloaded if the o/tool/sha256sum artifact becomes invalidated
#define PROG "sha256sum"
#define USAGE \
"\
Usage: " PROG " [-?hbctw] [PATH...]\n\
-h help\n\
-c check mode\n\
-b binary mode\n\
-t textual mode\n\
-w warning mode\n"
#define ROTR(a, b) (((a) >> (b)) | ((a) << (32 - (b))))
#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define EP1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define SIG0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ ((x) >> 3))
#define SIG1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ ((x) >> 10))
struct Sha256Ctx {
uint8_t data[64];
uint32_t datalen;
uint64_t bitlen;
uint32_t state[8];
};
static const uint32_t kSha256Tab[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, //
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, //
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, //
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, //
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, //
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, //
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, //
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, //
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, //
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, //
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, //
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, //
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, //
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, //
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, //
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, //
};
static bool g_warn;
static char g_mode;
static bool g_check;
static int g_mismatches;
static void Sha256Transform(uint32_t state[8], const uint8_t data[64]) {
unsigned i;
uint32_t a, b, c, d, e, f, g, h, t1, t2, m[64];
for (i = 0; i < 16; ++i, data += 4) {
m[i] = (uint32_t)data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
}
for (; i < 64; ++i) {
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
}
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
for (i = 0; i < 64; ++i) {
t1 = h + EP1(e) + CH(e, f, g) + kSha256Tab[i] + m[i];
t2 = EP0(a) + MAJ(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
}
static void Sha256Init(struct Sha256Ctx *ctx) {
ctx->datalen = 0;
ctx->bitlen = 0;
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
static void Sha256Update(struct Sha256Ctx *ctx, const uint8_t *data,
long size) {
long i;
for (i = 0; i < size; ++i) {
ctx->data[ctx->datalen] = data[i];
ctx->datalen++;
if (ctx->datalen == 64) {
Sha256Transform(ctx->state, ctx->data);
ctx->bitlen += 512;
ctx->datalen = 0;
}
}
}
static void Sha256Final(struct Sha256Ctx *ctx, uint8_t *hash) {
long i;
i = ctx->datalen;
ctx->data[i++] = 0x80;
if (ctx->datalen < 56) {
memset(ctx->data + i, 0, 56 - i);
} else {
memset(ctx->data + i, 0, 64 - i);
Sha256Transform(ctx->state, ctx->data);
memset(ctx->data, 0, 56);
}
ctx->bitlen += ctx->datalen * 8;
ctx->data[63] = ctx->bitlen;
ctx->data[62] = ctx->bitlen >> 8;
ctx->data[61] = ctx->bitlen >> 16;
ctx->data[60] = ctx->bitlen >> 24;
ctx->data[59] = ctx->bitlen >> 32;
ctx->data[58] = ctx->bitlen >> 40;
ctx->data[57] = ctx->bitlen >> 48;
ctx->data[56] = ctx->bitlen >> 56;
Sha256Transform(ctx->state, ctx->data);
for (i = 0; i < 4; ++i) {
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0xff;
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0xff;
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0xff;
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0xff;
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0xff;
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0xff;
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0xff;
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0xff;
}
}
static char *FormatUint32(char *p, uint32_t x) {
char t;
size_t i, a, b;
i = 0;
do {
p[i++] = x % 10 + '0';
x = x / 10;
} while (x > 0);
p[i] = '\0';
if (i) {
for (a = 0, b = i - 1; a < b; ++a, --b) {
t = p[a];
p[a] = p[b];
p[b] = t;
}
}
return p + i;
}
static char *FormatInt32(char *p, int32_t x) {
if (x < 0)
*p++ = '-', x = -(uint32_t)x;
return FormatUint32(p, x);
}
static size_t StrCat(char *dst, const char *src, size_t dsize) {
size_t m, n = dsize;
const char *p = dst;
const char *q = src;
while (n-- != 0 && *dst != '\0')
dst++;
m = dst - p;
n = dsize - m;
if (n-- == 0) {
return m + strlen(src);
}
while (*src != '\0') {
if (n != 0) {
*dst++ = *src;
n--;
}
src++;
}
*dst = '\0';
return m + (src - q);
}
static void GetOpts(int argc, char *argv[]) {
int opt;
g_mode = ' ';
while ((opt = getopt(argc, argv, "?hbctw")) != -1) {
switch (opt) {
case 'w':
g_warn = true;
break;
case 'c':
g_check = true;
break;
case 't':
g_mode = ' ';
break;
case 'b':
g_mode = '*';
break;
case 'h':
case '?':
(void)write(1, USAGE, sizeof(USAGE) - 1);
exit(0);
default:
(void)write(2, USAGE, sizeof(USAGE) - 1);
exit(64);
}
}
}
static void Write(int fd, const char *s, ...) {
va_list va;
char buf[512];
buf[0] = 0;
va_start(va, s);
do {
StrCat(buf, s, sizeof(buf));
} while ((s = va_arg(va, const char *)));
va_end(va);
(void)write(fd, buf, strlen(buf));
}
static bool IsModeCharacter(char c) {
switch (c) {
case ' ':
case '*':
return true;
default:
return false;
}
}
static bool IsSupportedPath(const char *path) {
size_t i;
for (i = 0;; ++i) {
switch (path[i]) {
case 0:
if (i)
return true;
// fallthrough
case '\r':
case '\n':
case '\\':
Write(2, PROG, ": ", path, ": unsupported path\n", NULL);
return false;
default:
break;
}
}
}
static bool GetDigest(const char *path, FILE *f, uint8_t digest[32]) {
size_t got;
uint8_t buf[512];
struct Sha256Ctx ctx;
Sha256Init(&ctx);
while ((got = fread(buf, 1, sizeof(buf), f))) {
Sha256Update(&ctx, buf, got);
}
if (ferror(f)) {
Write(2, PROG, ": ", path, ": ", strerror(errno), "\n", NULL);
return false;
}
Sha256Final(&ctx, digest);
return true;
}
static char *CopyHex(char *s, const void *p, size_t n) {
const char *d, *e;
for (d = (const char *)p, e = d + n; d < e; ++d) {
*s++ = "0123456789abcdef"[(*d >> 4) & 15];
*s++ = "0123456789abcdef"[(*d >> 0) & 15];
}
*s = 0;
return s;
}
static bool ProduceDigest(const char *path, FILE *f) {
char hexdigest[65];
char mode[2] = {g_mode};
unsigned char digest[32];
if (!IsSupportedPath(path))
return false;
if (!GetDigest(path, f, digest))
return false;
CopyHex(hexdigest, digest, 32);
Write(1, hexdigest, " ", mode, path, "\n", NULL);
return true;
}
static char *Chomp(char *line) {
size_t i;
if (line) {
for (i = strlen(line); i--;) {
if (line[i] == '\r' || line[i] == '\n') {
line[i] = '\0';
} else {
break;
}
}
}
return line;
}
static int HexToInt(int c) {
if ('0' <= c && c <= '9') {
return c - '0';
} else if ('a' <= c && c <= 'f') {
return c - 'a' + 10;
} else if ('A' <= c && c <= 'F') {
return c - 'A' + 10;
} else {
return -1;
}
}
static bool CheckDigests(const char *path, FILE *f) {
FILE *f2;
bool k = true;
int a, b, i, line;
const char *path2, *status;
uint8_t wantdigest[32], gotdigest[32];
char buf[64 + 2 + PATH_MAX + 1 + 1], *p;
for (line = 0; fgets(buf, sizeof(buf), f); ++line) {
if (!*Chomp(buf))
continue;
for (p = buf, i = 0; i < 32; ++i) {
if ((a = HexToInt(*p++ & 255)) == -1)
goto InvalidLine;
if ((b = HexToInt(*p++ & 255)) == -1)
goto InvalidLine;
wantdigest[i] = a << 4 | b;
}
if (*p++ != ' ')
goto InvalidLine;
if (!IsModeCharacter(*p++))
goto InvalidLine;
path2 = p;
if (!*path2)
goto InvalidLine;
if (!IsSupportedPath(path2))
continue;
if ((f2 = fopen(path2, "rb"))) {
if (GetDigest(path2, f2, gotdigest)) {
if (!memcmp(wantdigest, gotdigest, 32)) {
status = "OK";
} else {
status = "FAILED";
++g_mismatches;
k = false;
}
Write(1, path2, ": ", status, "\n", NULL);
} else {
k = false;
}
fclose(f2);
} else {
Write(2, PROG, ": ", path2, ": ", strerror(errno), "\n", NULL);
k = false;
}
continue;
InvalidLine:
if (g_warn) {
char linestr[12];
FormatInt32(linestr, line + 1);
Write(2, PROG, ": ", path, ":", linestr, ": ",
"improperly formatted checksum line", "\n", NULL);
}
}
if (ferror(f)) {
Write(2, PROG, ": ", path, ": ", strerror(errno), "\n", NULL);
k = false;
}
return k;
}
static bool Process(const char *path, FILE *f) {
if (g_check) {
return CheckDigests(path, f);
} else {
return ProduceDigest(path, f);
}
}
int main(int argc, char *argv[]) {
int i;
FILE *f;
bool k = true;
GetOpts(argc, argv);
if (optind == argc) {
f = stdin;
k &= Process("-", f);
} else {
for (i = optind; i < argc; ++i) {
if ((f = fopen(argv[i], "rb"))) {
k &= Process(argv[i], f);
fclose(f);
} else {
Write(2, PROG, ": ", argv[i], ": ", strerror(errno), "\n", NULL);
k = false;
}
}
}
if (g_mismatches) {
char ibuf[12];
FormatInt32(ibuf, g_mismatches);
Write(2, PROG, ": WARNING: ", ibuf, " computed checksum did NOT match\n",
NULL);
}
return !k;
}

View file

@ -1,13 +0,0 @@
---
BasedOnStyle: Mozilla
IndentWidth: 4
ColumnLimit: 80
---
Language: Cpp
AllowShortFunctionsOnASingleLine: false
AlignTrailingComments: false
AlignEscapedNewlines: DontAlign
AlwaysBreakTemplateDeclarations: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
FixNamespaceComments: true
---

View file

@ -1,55 +0,0 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
PKGS += CTL
CTL_ARTIFACTS += CTL_A
CTL = $(CTL_A_DEPS) $(CTL_A)
CTL_A = o/$(MODE)/ctl/ctl.a
CTL_A_FILES := $(wildcard ctl/*)
CTL_A_HDRS = $(filter %.h,$(CTL_A_FILES))
CTL_A_SRCS = $(filter %.cc,$(CTL_A_FILES))
CTL_A_OBJS = $(CTL_A_SRCS:%.cc=o/$(MODE)/%.o)
CTL_A_CHECKS = \
$(CTL_A).pkg \
$(CTL_A_HDRS:%=o/$(MODE)/%.okk) \
CTL_A_DIRECTDEPS = \
LIBC_INTRIN \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_STDIO \
LIBC_STR \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_GDTOA \
THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \
CTL_A_DEPS := $(call uniq,$(foreach x,$(CTL_A_DIRECTDEPS),$($(x))))
$(CTL_A): ctl/ \
$(CTL_A).pkg \
$(CTL_A_OBJS)
$(CTL_A).pkg: \
$(CTL_A_OBJS) \
$(foreach x,$(CTL_A_DIRECTDEPS),$($(x)_A).pkg)
$(CTL_A_OBJS): private \
OVERRIDE_CXXFLAGS += \
-Wframe-larger-than=4096 \
-Walloca-larger-than=4096 \
-ffunction-sections \
-fdata-sections \
-fexceptions \
CTL_LIBS = $(foreach x,$(CTL_ARTIFACTS),$($(x)))
CTL_SRCS = $(foreach x,$(CTL_ARTIFACTS),$($(x)_SRCS))
CTL_HDRS = $(foreach x,$(CTL_ARTIFACTS),$($(x)_HDRS))
CTL_CHECKS = $(foreach x,$(CTL_ARTIFACTS),$($(x)_CHECKS))
CTL_OBJS = $(foreach x,$(CTL_ARTIFACTS),$($(x)_OBJS))
$(CTL_OBJS): $(BUILD_FILES) ctl/BUILD.mk
.PHONY: o/$(MODE)/ctl
o/$(MODE)/ctl: $(CTL_CHECKS)

File diff suppressed because it is too large Load diff

View file

@ -1,28 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ACCUMULATE_H_
#define CTL_ACCUMULATE_H_
namespace ctl {
template<class InputIt, class T>
constexpr T
accumulate(InputIt first, InputIt last, T init)
{
for (; first != last; ++first)
init = init + *first;
return init;
}
template<class InputIt, class T, class BinaryOperation>
constexpr T
accumulate(InputIt first, InputIt last, T init, BinaryOperation op)
{
for (; first != last; ++first)
init = op(init, *first);
return init;
}
} // namespace ctl
#endif // CTL_ACCUMULATE_H_

View file

@ -1,55 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ADD_LVALUE_REFERENCE_H_
#define CTL_ADD_LVALUE_REFERENCE_H_
namespace ctl {
template<typename T>
struct add_lvalue_reference
{
typedef T& type;
};
template<typename T>
struct add_lvalue_reference<T&>
{
typedef T& type;
};
template<typename T>
struct add_lvalue_reference<T&&>
{
typedef T& type;
};
template<>
struct add_lvalue_reference<void>
{
typedef void type;
};
template<>
struct add_lvalue_reference<const void>
{
typedef const void type;
};
template<>
struct add_lvalue_reference<volatile void>
{
typedef volatile void type;
};
template<>
struct add_lvalue_reference<const volatile void>
{
typedef const volatile void type;
};
template<typename T>
using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;
} // namespace ctl
#endif // CTL_ADD_LVALUE_REFERENCE_H_

View file

@ -1,43 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ADD_POINTER_H_
#define CTL_ADD_POINTER_H_
#include "remove_reference.h"
namespace ctl {
// Primary template
template<typename T>
struct add_pointer
{
typedef typename remove_reference<T>::type* type;
};
// Specialization for functions
template<typename R, typename... Args>
struct add_pointer<R(Args...)>
{
typedef R (*type)(Args...);
};
// Specialization for function references
template<typename R, typename... Args>
struct add_pointer<R (&)(Args...)>
{
typedef R (*type)(Args...);
};
// Specialization for rvalue references to functions
template<typename R, typename... Args>
struct add_pointer<R (&&)(Args...)>
{
typedef R (*type)(Args...);
};
// Helper alias template (C++14 and later)
template<typename T>
using add_pointer_t = typename add_pointer<T>::type;
} // namespace ctl
#endif // CTL_ADD_POINTER_H_

View file

@ -1,29 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ADDRESSOF_H_
#define CTL_ADDRESSOF_H_
namespace ctl {
template<typename T>
T*
addressof(T& arg) noexcept
{
return reinterpret_cast<T*>(
&const_cast<char&>(reinterpret_cast<const volatile char&>(arg)));
}
template<typename R, typename... Args>
R (*addressof(R (*&arg)(Args...)) noexcept)
(Args...)
{
return arg;
}
template<typename T>
T*
addressof(T&&) = delete;
} // namespace ctl
#endif // CTL_ADDRESSOF_H_

View file

@ -1,24 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ADVANCE_H_
#define CTL_ADVANCE_H_
namespace ctl {
template<class InputIt, class Distance>
constexpr void
advance(InputIt& it, Distance n)
{
while (n > 0) {
--n;
++it;
}
while (n < 0) {
++n;
--it;
}
}
} // namespace ctl
#endif // CTL_ADVANCE_H_

View file

@ -1,20 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ALL_OF_H_
#define CTL_ALL_OF_H_
namespace ctl {
template<class InputIt, class UnaryPredicate>
constexpr bool
all_of(InputIt first, InputIt last, UnaryPredicate p)
{
for (; first != last; ++first)
if (!p(*first))
return false;
return true;
}
} // namespace ctl
#endif // CTL_ALL_OF_H_

View file

@ -1,94 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ALLOCATOR_H_
#define CTL_ALLOCATOR_H_
#include "bad_alloc.h"
#include "integral_constant.h"
#include "new.h"
#include "utility.h"
namespace ctl {
template<typename T>
class allocator
{
public:
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using propagate_on_container_move_assignment = ctl::true_type;
using is_always_equal = ctl::true_type;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
constexpr allocator() noexcept = default;
constexpr allocator(const allocator&) noexcept = default;
template<class U>
constexpr allocator(const allocator<U>&) noexcept
{
}
constexpr ~allocator() = default;
[[nodiscard]] T* allocate(size_type n)
{
if (n > __SIZE_MAX__ / sizeof(T))
throw ctl::bad_alloc();
if (auto p = static_cast<T*>(::operator new(
n * sizeof(T), ctl::align_val_t(alignof(T)), ctl::nothrow)))
return p;
throw ctl::bad_alloc();
}
void deallocate(T* p, size_type n) noexcept
{
::operator delete(p, n * sizeof(T), ctl::align_val_t(alignof(T)));
}
template<typename U, typename... Args>
void construct(U* p, Args&&... args)
{
::new (static_cast<void*>(p)) U(ctl::forward<Args>(args)...);
}
template<typename U>
void destroy(U* p)
{
p->~U();
}
size_type max_size() const noexcept
{
return __SIZE_MAX__ / sizeof(T);
}
allocator& operator=(const allocator&) = default;
template<typename U>
struct rebind
{
using other = allocator<U>;
};
};
template<class T, class U>
bool
operator==(const allocator<T>&, const allocator<U>&) noexcept
{
return true;
}
template<class T, class U>
bool
operator!=(const allocator<T>&, const allocator<U>&) noexcept
{
return false;
}
} // namespace ctl
#endif // CTL_ALLOCATOR_H_

View file

@ -1,70 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ALLOCATOR_TRAITS_H_
#define CTL_ALLOCATOR_TRAITS_H_
#include "integral_constant.h"
namespace ctl {
template<typename Alloc>
struct allocator_traits
{
using allocator_type = Alloc;
using value_type = typename Alloc::value_type;
using pointer = typename Alloc::value_type*;
using const_pointer = const typename Alloc::value_type*;
using void_pointer = void*;
using const_void_pointer = const void*;
using difference_type = ptrdiff_t;
using size_type = size_t;
using propagate_on_container_copy_assignment = ctl::false_type;
using propagate_on_container_move_assignment = ctl::true_type;
using propagate_on_container_swap = ctl::false_type;
using is_always_equal = ctl::true_type;
template<typename T>
struct rebind_alloc
{
using other = typename Alloc::template rebind<T>::other;
};
template<typename T>
using rebind_traits = allocator_traits<typename rebind_alloc<T>::other>;
static pointer allocate(Alloc& a, size_type n)
{
return a.allocate(n);
}
static void deallocate(Alloc& a, pointer p, size_type n)
{
a.deallocate(p, n);
}
template<typename T, typename... Args>
static void construct(Alloc& a, T* p, Args&&... args)
{
::new ((void*)p) T(static_cast<Args&&>(args)...);
}
template<typename T>
static void destroy(Alloc& a, T* p)
{
p->~T();
}
static size_type max_size(const Alloc& a) noexcept
{
return a.max_size();
}
static Alloc select_on_container_copy_construction(const Alloc& a)
{
return a;
}
};
} // namespace ctl
#endif // CTL_ALLOCATOR_TRAITS_H_

View file

@ -1,20 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ANY_OF_H_
#define CTL_ANY_OF_H_
namespace ctl {
template<class InputIt, class UnaryPredicate>
constexpr bool
any_of(InputIt first, InputIt last, UnaryPredicate p)
{
for (; first != last; ++first)
if (p(*first))
return true;
return false;
}
} // namespace ctl
#endif // CTL_ANY_OF_H_

View file

@ -1,248 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_ARRAY_H_
#define CTL_ARRAY_H_
#include "initializer_list.h"
#include "out_of_range.h"
#include "reverse_iterator.h"
namespace ctl {
template<typename T, size_t N>
struct array
{
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = value_type*;
using const_iterator = const value_type*;
using reverse_iterator = ctl::reverse_iterator<iterator>;
using const_reverse_iterator = ctl::reverse_iterator<const_iterator>;
T elems[N];
constexpr array() = default;
constexpr array(std::initializer_list<T> init)
{
auto it = init.begin();
for (size_t i = 0; i < N && it != init.end(); ++i, ++it)
elems[i] = *it;
}
constexpr reference at(size_type pos)
{
if (pos >= N)
throw ctl::out_of_range();
return elems[pos];
}
constexpr const_reference at(size_type pos) const
{
if (pos >= N)
throw ctl::out_of_range();
return elems[pos];
}
constexpr reference operator[](size_type pos)
{
if (pos >= N)
__builtin_trap();
return elems[pos];
}
constexpr const_reference operator[](size_type pos) const
{
if (pos >= N)
__builtin_trap();
return elems[pos];
}
constexpr reference front()
{
return elems[0];
}
constexpr const_reference front() const
{
return elems[0];
}
constexpr reference back()
{
return elems[N - 1];
}
constexpr const_reference back() const
{
return elems[N - 1];
}
constexpr T* data() noexcept
{
return elems;
}
constexpr const T* data() const noexcept
{
return elems;
}
constexpr iterator begin() noexcept
{
return elems;
}
constexpr const_iterator begin() const noexcept
{
return elems;
}
constexpr iterator end() noexcept
{
return elems + N;
}
constexpr const_iterator end() const noexcept
{
return elems + N;
}
constexpr reverse_iterator rbegin() noexcept
{
return reverse_iterator(end());
}
constexpr const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
constexpr reverse_iterator rend() noexcept
{
return reverse_iterator(begin());
}
constexpr const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
constexpr const_iterator cbegin() const noexcept
{
return begin();
}
constexpr const_iterator cend() const noexcept
{
return end();
}
constexpr const_reverse_iterator crbegin() const noexcept
{
return rbegin();
}
constexpr const_reverse_iterator crend() const noexcept
{
return rend();
}
constexpr bool empty() const noexcept
{
return N == 0;
}
constexpr size_type size() const noexcept
{
return N;
}
constexpr size_type max_size() const noexcept
{
return N;
}
void fill(const T& value)
{
for (size_type i = 0; i < N; ++i) {
elems[i] = value;
}
}
void swap(array& other) noexcept
{
for (size_type i = 0; i < N; ++i) {
T temp = elems[i];
elems[i] = other.elems[i];
other.elems[i] = temp;
}
}
};
template<typename T, size_t N>
bool
operator==(const array<T, N>& lhs, const array<T, N>& rhs)
{
for (size_t i = 0; i < N; ++i) {
if (!(lhs[i] == rhs[i]))
return false;
}
return true;
}
template<typename T, size_t N>
bool
operator!=(const array<T, N>& lhs, const array<T, N>& rhs)
{
return !(lhs == rhs);
}
template<typename T, size_t N>
bool
operator<(const array<T, N>& lhs, const array<T, N>& rhs)
{
for (size_t i = 0; i < N; ++i) {
if (lhs[i] < rhs[i])
return true;
if (rhs[i] < lhs[i])
return false;
}
return false;
}
template<typename T, size_t N>
bool
operator<=(const array<T, N>& lhs, const array<T, N>& rhs)
{
return !(rhs < lhs);
}
template<typename T, size_t N>
bool
operator>(const array<T, N>& lhs, const array<T, N>& rhs)
{
return rhs < lhs;
}
template<typename T, size_t N>
bool
operator>=(const array<T, N>& lhs, const array<T, N>& rhs)
{
return !(lhs < rhs);
}
template<typename T, size_t N>
void
swap(array<T, N>& lhs, array<T, N>& rhs) noexcept
{
lhs.swap(rhs);
}
} // namespace ctl
#endif // CTL_ARRAY_H_

View file

@ -1,64 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_BACK_INSERTER_H_
#define CTL_BACK_INSERTER_H_
#include "iterator.h"
#include "utility.h"
namespace ctl {
template<class Container>
class back_insert_iterator
{
protected:
Container* container;
public:
using iterator_category = ctl::output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
explicit back_insert_iterator(Container& c) : container(&c)
{
}
back_insert_iterator& operator=(const typename Container::value_type& value)
{
container->push_back(value);
return *this;
}
back_insert_iterator& operator=(typename Container::value_type&& value)
{
container->push_back(ctl::move(value));
return *this;
}
back_insert_iterator& operator*()
{
return *this;
}
back_insert_iterator& operator++()
{
return *this;
}
back_insert_iterator operator++(int)
{
return *this;
}
};
template<class Container>
back_insert_iterator<Container>
back_inserter(Container& c)
{
return back_insert_iterator<Container>(c);
}
} // namespace ctl
#endif // CTL_BACK_INSERTER_H_

View file

@ -1,23 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_BAD_ALLOC_H_
#define CTL_BAD_ALLOC_H_
#include "exception.h"
namespace ctl {
class bad_alloc : public ctl::exception
{
public:
bad_alloc() noexcept = default;
~bad_alloc() override = default;
const char* what() const noexcept override
{
return "ctl::bad_alloc";
}
};
} // namespace ctl
#endif // CTL_BAD_ALLOC_H_

View file

@ -1,25 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_CONDITIONAL_H_
#define CTL_CONDITIONAL_H_
namespace ctl {
template<bool B, class T, class F>
struct conditional
{
typedef T type;
};
template<class T, class F>
struct conditional<false, T, F>
{
typedef F type;
};
template<bool B, typename T, typename F>
using conditional_t = typename conditional<B, T, F>::type;
} // namespace ctl
#endif // CTL_CONDITIONAL_H_

View file

@ -1,22 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_COPY_H_
#define CTL_COPY_H_
namespace ctl {
template<class InputIt, class OutputIt>
OutputIt
copy(InputIt first, InputIt last, OutputIt d_first)
{
while (first != last) {
*d_first = *first;
++d_first;
++first;
}
return d_first;
}
} // namespace ctl
#endif // CTL_COPY_H_

View file

@ -1,36 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_DECAY_H_
#define CTL_DECAY_H_
#include "add_pointer.h"
#include "conditional.h"
#include "is_array.h"
#include "is_function.h"
#include "remove_cv.h"
#include "remove_extent.h"
#include "remove_reference.h"
namespace ctl {
template<typename T>
struct decay
{
private:
typedef typename ctl::remove_reference<T>::type U;
public:
typedef typename ctl::conditional<
ctl::is_array<U>::value,
typename ctl::remove_extent<U>::type*,
typename ctl::conditional<ctl::is_function<U>::value,
typename ctl::add_pointer<U>::type,
typename ctl::remove_cv<U>::type>::type>::type
type;
};
template<typename T>
using decay_t = typename decay<T>::type;
} // namespace ctl
#endif // CTL_DECAY_H_

View file

@ -1,44 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_DEFAULT_DELETE_H_
#define CTL_DEFAULT_DELETE_H_
#include "enable_if.h"
#include "is_convertible.h"
namespace ctl {
template<typename T>
struct default_delete
{
constexpr default_delete() noexcept = default;
template<typename U,
typename = typename ctl::enable_if<
ctl::is_convertible<U*, T*>::value>::type>
constexpr default_delete(const default_delete<U>&) noexcept
{
}
constexpr void operator()(T* const p) const noexcept
{
delete p;
}
};
template<typename T>
struct default_delete<T[]>
{
constexpr default_delete() noexcept = default;
template<typename U,
typename = typename ctl::enable_if<
ctl::is_convertible<U (*)[], T (*)[]>::value>::type>
constexpr default_delete(const default_delete<U[]>&) noexcept
{
}
constexpr void operator()(T* const p) const noexcept
{
delete[] p;
}
};
} // namespace ctl
#endif // CTL_DEFAULT_DELETE_H_

View file

@ -1,39 +0,0 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_DISTANCE_H_
#define CTL_DISTANCE_H_
#include "iterator.h"
#include "iterator_traits.h"
namespace ctl {
template<class InputIter>
constexpr typename ctl::iterator_traits<InputIter>::difference_type
distance_impl(InputIter first, InputIter last, input_iterator_tag)
{
typename ctl::iterator_traits<InputIter>::difference_type res(0);
for (; first != last; ++first)
++res;
return res;
}
template<class RandIter>
constexpr typename ctl::iterator_traits<RandIter>::difference_type
distance_impl(RandIter first, RandIter last, random_access_iterator_tag)
{
return last - first;
}
template<class InputIter>
constexpr typename ctl::iterator_traits<InputIter>::difference_type
distance(InputIter first, InputIter last)
{
return distance_impl(
first,
last,
typename ctl::iterator_traits<InputIter>::iterator_category());
}
} // namespace ctl
#endif // CTL_DISTANCE_H_

Some files were not shown because too many files have changed in this diff Show more