commit 094156a1e259ac347e57698d0ba98e2f708e17f2
parent 51067d58526abd950478d1e632893fbda63918d5
Author: Alexey Yerin <yyp@disroot.org>
Date: Mon, 31 Jan 2022 22:10:43 +0300
+libc: fix regressions caused by unique @init names
Since 7f428e7@harec[0], @init and @fini functions will have generated
names and as such not work when called directly, which is what
rt::start_linux on +libc did.
This also fixes a possible issue in os:: and linux::, which relied on
local @init names sorted alphabetically to call rt::start_linux. Not
all libc implementations might do so and cause unexpected problems.
[0]: https://git.sr.ht/~sircmpwn/harec/commit/7f428e7c869bef3d52d12ccdaf8d7002d4fae9e9
Fixes: https://todo.sr.ht/~sircmpwn/hare/566
Signed-off-by: Alexey Yerin <yyp@disroot.org>
Diffstat:
5 files changed, 133 insertions(+), 11 deletions(-)
diff --git a/linux/+libc.ha b/linux/+libc.ha
@@ -1,5 +0,0 @@
-use rt;
-
-@init fn ensure_rt_init() void = {
- rt::start_linux();
-};
diff --git a/linux/start+libc.ha b/linux/start+libc.ha
@@ -0,0 +1,12 @@
+use rt;
+use format::elf;
+
+@init fn init_linux() void = {
+ rt::start_linux();
+
+ let i = 0;
+ for (rt::envp[i] != null) {
+ i += 1;
+ };
+ auxv = &rt::envp[i + 1]: *[*]elf::auxv64;
+};
diff --git a/os/+linux/+libc.ha b/os/+linux/+libc.ha
@@ -1,5 +0,0 @@
-use rt;
-
-@init fn ensure_rt_init() void = {
- rt::start_linux();
-};
diff --git a/os/+linux/environ+libc.ha b/os/+linux/environ+libc.ha
@@ -0,0 +1,118 @@
+use bytes;
+use rt;
+use strings;
+use types;
+
+// The command line arguments provided to the program. By convention, the first
+// member is usually the name of the program.
+export let args: []str = [];
+
+// Statically allocate arg strings if there are few enough arguments, saves a
+// syscall if we don't need it.
+let args_static: [32]str = [""...];
+
+@init fn init_environ() void = {
+ rt::start_linux();
+ if (rt::argc < len(args_static)) {
+ args = args_static[..rt::argc];
+ for (let i = 0z; i < rt::argc; i += 1) {
+ args[i] = strings::fromc(rt::argv[i]);
+ };
+ } else {
+ args = alloc([], rt::argc);
+ for (let i = 0z; i < rt::argc; i += 1) {
+ append(args, strings::fromc(rt::argv[i]));
+ };
+ };
+
+};
+
+@fini fn fini_environ() void = {
+ if (rt::argc >= len(args_static)) {
+ free(args);
+ };
+};
+
+// Looks up an environment variable and returns its value, or void if unset.
+export fn getenv(name: const str) (str | void) = {
+ const name_b = strings::toutf8(name);
+ for (let i = 0z; rt::envp[i] != null; i += 1) {
+ const item = rt::envp[i]: *[*]u8;
+ const ln = strings::cstrlen(item: *char);
+ const eq: size = match (bytes::index(item[..ln], '=': u8)) {
+ case void =>
+ abort("Environment violates System-V invariants");
+ case let i: size =>
+ yield i;
+ };
+ if (bytes::equal(name_b, item[..eq])) {
+ const ln = strings::cstrlen(item: *const char);
+ return strings::fromutf8(item[eq+1..ln]);
+ };
+ };
+};
+
+// Looks up an environment variable and returns its value, or a default value if
+// unset.
+export fn tryenv(name: const str, default: str) str = match (getenv(name)) {
+case let s: str =>
+ yield s;
+case void =>
+ yield default;
+};
+
+let envp: []str = [];
+
+// Returns a slice of the environment strings in the form KEY=VALUE.
+export fn getenvs() []str = {
+ if (len(envp) != 0) {
+ return envp;
+ };
+ for (let i = 0z; rt::envp[i] != null; i += 1) {
+ append(envp, strings::fromc(rt::envp[i]: *const char));
+ };
+ return envp;
+};
+
+let uts: rt::utsname = rt::utsname { ... };
+let uts_valid: bool = false;
+
+// Returns the host kernel name
+export fn sysname() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ };
+ return strings::fromc(&uts.sysname: *const char);
+};
+
+// Returns the host system hostname
+export fn hostname() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ };
+ return strings::fromc(&uts.nodename: *const char);
+};
+
+// Returns the host kernel version
+export fn release() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ };
+ return strings::fromc(&uts.release: *const char);
+};
+
+// Returns the host operating system version
+export fn version() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ };
+ return strings::fromc(&uts.version: *const char);
+};
+
+// Returns the host CPU architecture
+export fn machine() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ };
+ return strings::fromc(&uts.machine: *const char);
+};
diff --git a/rt/+linux/platformstart+libc.ha b/rt/+linux/platformstart+libc.ha
@@ -1,4 +1,4 @@
-export @init fn start_linux() void = {
+export fn start_linux() void = {
// Here we use a cool strategy of re-constructing argv and argc without
// knowing their original values. Since environ is placed just after
// them, it's possible to traverse backwards calculating how many
@@ -15,4 +15,6 @@ export @init fn start_linux() void = {
envp = c_environ;
};
+@init fn start_linux() void = start_linux();
+
let @symbol("environ") c_environ: *[*]nullable *char;