summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile1
-rwxr-xr-xbuild-aux/stack.c.gen81
-rw-r--r--lib9p/9p.c32
-rwxr-xr-xlib9p/idl.gen44
-rw-r--r--lib9p/internal.h6
-rw-r--r--lib9p/map.h4
-rw-r--r--lib9p/srv.c10
7 files changed, 119 insertions, 59 deletions
diff --git a/GNUmakefile b/GNUmakefile
index db7f7fd..4b68f3f 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -103,6 +103,7 @@ lint/python3: lint/%: build-aux/venv
./build-aux/venv/bin/mypy --strict --scripts-are-modules $(sources_$*)
./build-aux/venv/bin/black --check $(sources_$*)
./build-aux/venv/bin/isort --check $(sources_$*)
+ ! grep -nh 'SPECIAL$$' -- lib9p/idl.gen
lint/c: lint/%: build-aux/lint-h build-aux/get-dscname
./build-aux/lint-h $(filter %.h,$(sources_$*))
lint/make lint/cmake lint/gitignore lint/ini lint/9p lint/markdown: lint/%:
diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen
index e327298..af5d740 100755
--- a/build-aux/stack.c.gen
+++ b/build-aux/stack.c.gen
@@ -135,7 +135,7 @@ def analyze(
*,
ci_fnames: list[str],
extra_nodes: list[Node] = [],
- app_func_filters: dict[str, typing.Callable[[str], bool]],
+ app_func_filters: dict[str, typing.Callable[[str], int]],
app_location_xform: typing.Callable[[str], str],
app_indirect_callees: typing.Callable[[VCGElem], list[str]],
app_skip_call: typing.Callable[[list[str], str], bool],
@@ -289,12 +289,12 @@ def analyze(
nsum = 0
rows: dict[str, int] = {}
for funcname in graph:
- if grp_filter(funcname):
+ if cnt := grp_filter(funcname):
n = nstatic(funcname)
rows[app_location_xform(funcname)] = n
if n > nmax:
nmax = n
- nsum += n
+ nsum += cnt * n
# Figure sizes.
namelen = max([len(k) for k in rows.keys()] + [len(grp_name) + 4])
@@ -314,7 +314,7 @@ def analyze(
for funcname in sorted(missing):
print(f"warning: missing: {funcname}")
for funcname in sorted(dynamic):
- print(f"warning: dynamic: {funcname}")
+ print(f"warning: dynamic-stack-usage: {funcname}")
print("*/")
@@ -431,9 +431,14 @@ def main(
hooks_indirect_callees += [sbc_indirect_callees]
- def sbc_is_thread(name: str) -> bool:
+ def sbc_is_thread(name: str) -> int:
if name.endswith("_cr") and name != "lib9p_srv_read_cr":
- return True
+ if "9p" in name:
+ if "read" in name:
+ return 8
+ elif "write" in name:
+ return 16
+ return 1
if name == "main":
return True
return False
@@ -721,51 +726,67 @@ def main(
# newlib #########################################################
if arg_pico_platform == "rp2040":
+ # This is accurate to
+ # /usr/arm-none-eabi/lib/thumb/v6-m/nofp/libg.a as of
+ # Parabola's arm-none-eabi-newlib 4.5.0.20241231-1.
all_nodes += [
# malloc
- synthetic_node("free", 0), # TODO
- synthetic_node("malloc", 0), # TODO
- synthetic_node("realloc", 0), # TODO
- synthetic_node("aligned_alloc", 0), # TODO
- synthetic_node("reallocarray", 0), # TODO
+ synthetic_node("free", 8, {"_free_r"}),
+ synthetic_node("malloc", 8, {"_malloc_r"}),
+ synthetic_node("realloc", 8, {"_realloc_r"}),
+ synthetic_node("aligned_alloc", 8, {"_memalign_r"}),
+ synthetic_node("reallocarray", 24, {"realloc", "__errno"}),
+ synthetic_node("_free_r", 0), # TODO
+ synthetic_node("_malloc_r", 0), # TODO
+ synthetic_node("_realloc_r", 0), # TODO
+ synthetic_node("_memalign_r", 0), # TODO
# execution
- synthetic_node("abort", 0), # TODO
- synthetic_node("longjmp", 0), # TODO
- synthetic_node("setjmp", 0), # TODO
+ synthetic_node("raise", 16, {"_getpid_r"}),
+ synthetic_node("abort", 8, {"raise", "_exit"}),
+ synthetic_node("longjmp", 0),
+ synthetic_node("setjmp", 0),
# <strings.h>
- synthetic_node("memcmp", 0), # TODO
- synthetic_node("memcpy", 0), # TODO
- synthetic_node("memset", 0), # TODO
- synthetic_node("strcmp", 0), # TODO
- synthetic_node("strlen", 0), # TODO
- synthetic_node("strncpy", 0), # TODO
- synthetic_node("strnlen", 0), # TODO
+ synthetic_node("memcmp", 12),
+ synthetic_node("memcpy", 28),
+ synthetic_node("memset", 20),
+ synthetic_node("strcmp", 16),
+ synthetic_node("strlen", 8),
+ synthetic_node("strncpy", 16),
+ synthetic_node("strnlen", 8),
# other
- synthetic_node("random", 0), # TODO
+ synthetic_node("__errno", 0),
+ synthetic_node("_getpid_r", 8, {"_getpid"}),
+ synthetic_node("random", 8),
]
# libgcc #########################################################
if arg_pico_platform == "rp2040":
+ # This is accurate to
+ # /usr/lib/gcc/arm-none-eabi/14.2.0/thumb/v6-m/nofp/libgcc.a
+ # as of Parabola's arm-none-eabi-gcc 14.2.0-1.
all_nodes += [
- synthetic_node("__aeabi_idiv0", 0), # TODO
- synthetic_node("__aeabi_ldiv0", 0), # TODO
+ synthetic_node("__aeabi_idiv0", 0),
+ synthetic_node("__aeabi_ldiv0", 0),
+ synthetic_node("__aeabi_llsr", 0),
]
# Tie it all together ############################################
- def thread_filter(name: str) -> bool:
+ def thread_filter(name: str) -> int:
return sbc_is_thread(name)
- def intrhandler_filter(name: str) -> bool:
+ def intrhandler_filter(name: str) -> int:
name = name.rsplit(":", 1)[-1]
for hook in hooks_is_intrhandler:
if hook(name):
- return True
- return False
+ return 1
+ return 0
- def misc_filter(name: str) -> bool:
- return name.endswith(":__lm_printf") or name == "__assert_msg_fail"
+ def misc_filter(name: str) -> int:
+ if name.endswith(":__lm_printf") or name == "__assert_msg_fail":
+ return 1
+ return 0
def location_xform(loc: str) -> str:
if not loc.startswith("/"):
diff --git a/lib9p/9p.c b/lib9p/9p.c
index 51ff2eb..c54dae7 100644
--- a/lib9p/9p.c
+++ b/lib9p/9p.c
@@ -124,10 +124,12 @@ ssize_t _lib9p_validate(uint8_t xxx_low_typ_bit,
struct lib9p_ctx *ctx, uint8_t *net_bytes) {
/* Inspect the first 5 bytes ourselves. */
struct _validate_ctx subctx = {
+ /* input */
.ctx = ctx,
.net_size = uint32le_decode(net_bytes),
.net_bytes = net_bytes,
+ /* output */
.net_offset = 0,
.host_extra = 0,
};
@@ -171,17 +173,19 @@ static
void _lib9p_unmarshal(const struct _lib9p_recv_tentry xxx_table[LIB9P_VER_NUM][0x80],
struct lib9p_ctx *ctx, uint8_t *net_bytes,
enum lib9p_msg_type *ret_typ, void *ret_body) {
+ enum lib9p_msg_type typ = net_bytes[4];
+ *ret_typ = typ;
+ struct _lib9p_recv_tentry tentry = xxx_table[ctx->version][typ/2];
+
struct _unmarshal_ctx subctx = {
+ /* input */
.ctx = ctx,
.net_bytes = net_bytes,
+ /* output */
.net_offset = 0,
+ .extra = ret_body + tentry.basesize,
};
-
- enum lib9p_msg_type typ = net_bytes[4];
- *ret_typ = typ;
- struct _lib9p_recv_tentry tentry = xxx_table[ctx->version][typ/2];
- subctx.extra = ret_body + tentry.basesize;
tentry.unmarshal(&subctx, ret_body);
}
@@ -202,7 +206,10 @@ bool _lib9p_marshal(const struct _lib9p_send_tentry xxx_table[LIB9P_VER_NUM][0x8
struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *body,
uint8_t *ret_bytes) {
struct _marshal_ctx subctx = {
+ /* input */
.ctx = ctx,
+
+ /* ouptut */
.net_bytes = ret_bytes,
.net_offset = 0,
};
@@ -228,10 +235,12 @@ bool lib9p_Rmsg_marshal(struct lib9p_ctx *ctx, enum lib9p_msg_type typ, void *bo
bool lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes,
uint32_t *ret_net_size, ssize_t *ret_host_size) {
struct _validate_ctx subctx = {
+ /* input */
.ctx = ctx,
.net_size = net_size,
.net_bytes = net_bytes,
+ /* output */
.net_offset = 0,
.host_extra = 0,
};
@@ -248,11 +257,13 @@ bool lib9p_stat_validate(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_
uint32_t lib9p_stat_unmarshal(struct lib9p_ctx *ctx, uint8_t *net_bytes,
struct lib9p_stat *ret_obj, void *ret_extra) {
struct _unmarshal_ctx subctx = {
- .ctx = ctx,
- .net_bytes = net_bytes,
- .net_offset = 0,
+ /* input */
+ .ctx = ctx,
+ .net_bytes = net_bytes,
- .extra = ret_extra,
+ /* output */
+ .net_offset = 0,
+ .extra = ret_extra,
};
_lib9p_stat_unmarshal(&subctx, ret_obj);
return subctx.net_offset;
@@ -263,7 +274,10 @@ uint32_t lib9p_stat_marshal(struct lib9p_ctx *ctx, uint32_t max_net_size, struct
struct lib9p_ctx _ctx = *ctx;
_ctx.max_msg_size = max_net_size;
struct _marshal_ctx subctx = {
+ /* input */
.ctx = &_ctx,
+
+ /* output */
.net_bytes = ret_bytes,
.net_offset = 0,
};
diff --git a/lib9p/idl.gen b/lib9p/idl.gen
index 8782771..adb15ce 100755
--- a/lib9p/idl.gen
+++ b/lib9p/idl.gen
@@ -53,13 +53,13 @@ def c_ver_enum(ver: str) -> str:
return f"{idprefix.upper()}VER_{ver.replace('.', '_')}"
-def c_ver_ifdef(versions: set[str]) -> str:
+def c_ver_ifdef(versions: typing.Collection[str]) -> str:
return " || ".join(
f"CONFIG_9P_ENABLE_{v.replace('.', '_')}" for v in sorted(versions)
)
-def c_ver_cond(versions: set[str]) -> str:
+def c_ver_cond(versions: typing.Collection[str]) -> str:
if len(versions) == 1:
v = next(v for v in versions)
return f"is_ver(ctx, {v.replace('.', '_')})"
@@ -84,7 +84,7 @@ def c_typename(typ: idl.Type, parent: idl.StructMember | None = None) -> str:
raise ValueError(f"not a type: {typ.__class__.__name__}")
-def c_expr(expr: idl.Expr) -> str:
+def c_expr(expr: idl.Expr, lookup_sym: typing.Callable[[str], str]) -> str:
ret: list[str] = []
for tok in expr.tokens:
match tok:
@@ -92,14 +92,14 @@ def c_expr(expr: idl.Expr) -> str:
ret.append(tok.op)
case idl.ExprLit():
ret.append(str(tok.val))
- case idl.ExprSym(name="end"):
- ret.append("ctx->net_offset")
case idl.ExprSym(name="s32_max"):
ret.append("INT32_MAX")
case idl.ExprSym(name="s64_max"):
ret.append("INT64_MAX")
case idl.ExprSym():
- ret.append(f"_{tok.name[1:]}_offset")
+ ret.append(lookup_sym(tok.name))
+ case _:
+ assert False
return " ".join(ret)
@@ -411,7 +411,7 @@ def gen_c(versions: set[str], typs: list[idl.Type]) -> str:
ret += f"""
/* strings ********************************************************************/
-const char *_lib9p_table_ver_name[{c_ver_enum('NUM')}] = {{
+const char *_{idprefix}table_ver_name[{c_ver_enum('NUM')}] = {{
"""
for ver in ["unknown", *sorted(versions)]:
if ver in versions:
@@ -573,17 +573,26 @@ LM_ALWAYS_INLINE static bool validate_8(struct _validate_ctx *ctx) { return _val
# Pass 4 - validate ,max= and ,val= constraints
for member in typ.members:
+
+ def lookup_sym(sym: str) -> str:
+ match sym:
+ case "end":
+ return "ctx->net_offset"
+ case _:
+ assert sym.startswith("&")
+ return f"_{sym[1:]}_offset"
+
if member.max:
assert member.static_size
nbits = member.static_size * 8
ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
- ret += f"\t || ({{ uint{nbits}_t max = {c_expr(member.max)}; (((uint{nbits}_t){member.name}) > max) &&\n"
+ ret += f"\t || ({{ uint{nbits}_t max = {c_expr(member.max, lookup_sym)}; (((uint{nbits}_t){member.name}) > max) &&\n"
ret += f'\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.name} value is too large (%"PRIu{nbits}" > %"PRIu{nbits}")", {member.name}, max); }})\n'
if member.val:
assert member.static_size
nbits = member.static_size * 8
ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
- ret += f"\t || ({{ uint{nbits}_t exp = {c_expr(member.val)}; (((uint{nbits}_t){member.name}) != exp) &&\n"
+ ret += f"\t || ({{ uint{nbits}_t exp = {c_expr(member.val, lookup_sym)}; (((uint{nbits}_t){member.name}) != exp) &&\n"
ret += f'\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.name} value is wrong (actual:%"PRIu{nbits}" != correct:%"PRIu{nbits}")", (uint{nbits}_t){member.name}, exp); }})\n'
ret += ifdef_pop(1)
@@ -775,10 +784,19 @@ LM_ALWAYS_INLINE static bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val)
if member.val:
assert member.static_size
ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
+
+ def lookup_sym(sym: str) -> str:
+ match sym:
+ case "end":
+ return "ctx->net_offset"
+ case _:
+ assert sym.startswith("&")
+ return f"_{sym[1:]}_offset"
+
if member.static_size == 1:
- ret += f"\t || ({{ ctx->net_bytes[_{member.name}_offset] = {c_expr(member.val)}; false; }})\n"
+ ret += f"\t || ({{ ctx->net_bytes[_{member.name}_offset] = {c_expr(member.val, lookup_sym)}; false; }})\n"
else:
- ret += f"\t || ({{ uint{member.static_size*8}le_encode(&ctx->net_bytes[_{member.name}_offset], {c_expr(member.val)}); false; }})\n"
+ ret += f"\t || ({{ uint{member.static_size*8}le_encode(&ctx->net_bytes[_{member.name}_offset], {c_expr(member.val, lookup_sym)}); false; }})\n"
ret += ifdef_pop(1)
ret += "\t ;\n"
@@ -815,10 +833,10 @@ LM_ALWAYS_INLINE static bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val)
LM_FLATTEN bool _{idprefix}stat_validate(struct _validate_ctx *ctx) {{
\treturn validate_stat(ctx);
}}
-LM_FLATTEN void _{idprefix}stat_unmarshal(struct _unmarshal_ctx *ctx, struct lib9p_stat *out) {{
+LM_FLATTEN void _{idprefix}stat_unmarshal(struct _unmarshal_ctx *ctx, struct {idprefix}stat *out) {{
\tunmarshal_stat(ctx, out);
}}
-LM_FLATTEN bool _{idprefix}stat_marshal(struct _marshal_ctx *ctx, struct lib9p_stat *val) {{
+LM_FLATTEN bool _{idprefix}stat_marshal(struct _marshal_ctx *ctx, struct {idprefix}stat *val) {{
\treturn marshal_stat(ctx, val);
}}
"""
diff --git a/lib9p/internal.h b/lib9p/internal.h
index d939d46..a648532 100644
--- a/lib9p/internal.h
+++ b/lib9p/internal.h
@@ -39,10 +39,12 @@ static_assert(CONFIG_9P_MAX_HOSTMSG_SIZE <= SSIZE_MAX);
/* specialized contexts *******************************************************/
struct _validate_ctx {
+ /* input */
struct lib9p_ctx *ctx;
uint32_t net_size;
uint8_t *net_bytes;
+ /* output */
uint32_t net_offset;
/* Increment `host_extra` to pre-allocate space that is
* "extra" beyond sizeof(). */
@@ -51,9 +53,11 @@ struct _validate_ctx {
typedef bool (*_validate_fn_t)(struct _validate_ctx *ctx);
struct _unmarshal_ctx {
+ /* input */
struct lib9p_ctx *ctx;
uint8_t *net_bytes;
+ /* output */
uint32_t net_offset;
/* `extra` points to the beginning of unallocated space. */
void *extra;
@@ -61,8 +65,10 @@ struct _unmarshal_ctx {
typedef void (*_unmarshal_fn_t)(struct _unmarshal_ctx *ctx, void *out);
struct _marshal_ctx {
+ /* input */
struct lib9p_ctx *ctx;
+ /* output */
uint8_t *net_bytes;
uint32_t net_offset;
};
diff --git a/lib9p/map.h b/lib9p/map.h
index b59c83d..ab9564f 100644
--- a/lib9p/map.h
+++ b/lib9p/map.h
@@ -1,6 +1,6 @@
-/* lib9p/map.h - A really dumb map/dict data structur
+/* lib9p/map.h - A really dumb map/dict data structure
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
diff --git a/lib9p/srv.c b/lib9p/srv.c
index 0e58068..9837994 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -205,12 +205,12 @@ static void respond_error(struct _lib9p_srv_req *req) {
/* read coroutine *************************************************************/
-static bool read_at_least(lo_interface net_stream_conn fd, uint8_t *buf, size_t goal, size_t *done) {
+static bool read_exactly(lo_interface net_stream_conn fd, uint8_t *buf, size_t goal, size_t *done) {
assert(buf);
assert(goal);
assert(done);
while (*done < goal) {
- ssize_t r = LO_CALL(fd, read, &buf[*done], CONFIG_9P_MAX_MSG_SIZE - *done);
+ ssize_t r = LO_CALL(fd, read, &buf[*done], goal - *done);
if (r < 0) {
nonrespond_errorf("read: %s", net_strerror(-r));
return true;
@@ -263,14 +263,14 @@ static void handle_message(struct _lib9p_srv_req *ctx);
nextmsg:
/* Read the message. */
size_t done = 0;
- if (read_at_least(conn.fd, buf, 4, &done))
+ if (read_exactly(conn.fd, buf, 4, &done))
goto close;
size_t goal = uint32le_decode(buf);
if (goal < 7) {
nonrespond_errorf("T-message is impossibly small");
goto close;
}
- if (read_at_least(conn.fd, buf, 7, &done))
+ if (read_exactly(conn.fd, buf, 7, &done))
goto close;
struct _lib9p_srv_req req = {
.parent_sess = &sess,
@@ -292,7 +292,7 @@ static void handle_message(struct _lib9p_srv_req *ctx);
respond_error(&req);
goto nextmsg;
}
- if (read_at_least(conn.fd, buf, goal, &done))
+ if (read_exactly(conn.fd, buf, goal, &done))
goto close;
/* Handle the message... */