hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Dlinux/+libc.ha | 5-----
Alinux/start+libc.ha | 12++++++++++++
Dos/+linux/+libc.ha | 5-----
Aos/+linux/environ+libc.ha | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mrt/+linux/platformstart+libc.ha | 4+++-
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;