aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorturret <turret@duck.com>2023-11-19 18:57:55 -0600
committerturret <turret@duck.com>2023-11-19 18:57:55 -0600
commita8b2282eb88f24c3c5f461e1557fa2cf76ebc251 (patch)
tree67544659f7df20e0286cb82e17aa47520a601f14
downloaddiscord-bot-skeleton-a8b2282eb88f24c3c5f461e1557fa2cf76ebc251.tar.gz
discord-bot-skeleton-a8b2282eb88f24c3c5f461e1557fa2cf76ebc251.tar.bz2
discord-bot-skeleton-a8b2282eb88f24c3c5f461e1557fa2cf76ebc251.zip
Initial commit
- create subsystem system using clone syscall, shared memory, shared file descriptors - printk-like logging facility (TODO: console loglevel) - initcall system (like linux kernel) TODO: determine license factors: linker.ld, linux kernel licensing (some ideas are more liberally taken rather than paraphrased)
-rw-r--r--.gitignore2
-rw-r--r--Makefile21
-rw-r--r--include/config.h4
-rw-r--r--include/init.h23
-rw-r--r--include/log.h42
-rw-r--r--include/subsys.h7
-rw-r--r--include/util.h5
-rw-r--r--init/init.c60
-rw-r--r--init/log.c69
-rw-r--r--init/subsys.c55
-rw-r--r--init/util.c22
-rw-r--r--linker.ld251
-rw-r--r--net/net.c2
13 files changed, 563 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..16ba087
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.o
+app
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8a8503e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+CC = gcc
+CFLAGS = -std=c99 -O3 -g -Wall -Wextra -Wpedantic -masm=intel
+CFLAGS += -Iinclude -D_GNU_SOURCE -D_POSIX_C_SOURCE=200809L
+CFLAGS += -Wno-unused-result
+LDFLAGS = -Tlinker.ld -no-pie
+
+SRC = $(wildcard *.c **/*.c)
+OBJ = $(SRC:.c=.o)
+
+.PHONY: all clean
+
+all: app
+
+%.o: %.c
+ $(CC) -o $@ -c $< $(CFLAGS)
+
+app: $(OBJ)
+ $(CC) -o app $^ $(LDFLAGS)
+
+clean:
+ rm -rf $(OBJ) app
diff --git a/include/config.h b/include/config.h
new file mode 100644
index 0000000..4254edc
--- /dev/null
+++ b/include/config.h
@@ -0,0 +1,4 @@
+#define VERSION "0.0.1"
+#define NAME_SHORTHAND "TCDBF"
+#define NAME "turret.'s C Discord Bot Framework"
+
diff --git a/include/init.h b/include/init.h
new file mode 100644
index 0000000..c9d8cba
--- /dev/null
+++ b/include/init.h
@@ -0,0 +1,23 @@
+#ifndef __INIT_H
+#define __INIT_H
+
+typedef int (*initcall_t)(void);
+typedef initcall_t initcall_entry_t;
+
+#define __define_initcall(fn, id) \
+ static initcall_t __initcall_##fn##id \
+ __attribute__((used)) \
+ __attribute__((section(".initcall" #id ".init"))) = fn
+
+#define l1_initcall(fn) __define_initcall(fn, 1)
+#define l2_initcall(fn) __define_initcall(fn, 2)
+#define l3_initcall(fn) __define_initcall(fn, 3)
+#define l4_initcall(fn) __define_initcall(fn, 4)
+#define l5_initcall(fn) __define_initcall(fn, 5)
+
+static inline initcall_t initcall_from_entry(initcall_entry_t *entry)
+{
+ return *entry;
+}
+
+#endif
diff --git a/include/log.h b/include/log.h
new file mode 100644
index 0000000..52d45ff
--- /dev/null
+++ b/include/log.h
@@ -0,0 +1,42 @@
+
+#define LOG_SOH "\001"
+#define LOG_SOH_ASCII '\001'
+
+#define LOG_EMERG LOG_SOH "0"
+#define LOG_ALERT LOG_SOH "1"
+#define LOG_CRIT LOG_SOH "2"
+#define LOG_ERR LOG_SOH "3"
+#define LOG_WARNING LOG_SOH "4"
+#define LOG_NOTICE LOG_SOH "5"
+#define LOG_INFO LOG_SOH "6"
+#define LOG_DEBUG LOG_SOH "7"
+
+#define LOG_DEFAULT ""
+
+int print(const char *fmt, ...);
+
+#define ANSI_CSI "\x1b["
+
+#define ANSI_BOLD ANSI_CSI "1m"
+#define ANSI_ITALIC ANSI_CSI "3m"
+#define ANSI_BLINK ANSI_CSI "5m"
+#define ANSI_REVERSE ANSI_CSI "7m"
+#define ANSI_RESET ANSI_CSI "0m"
+
+#define ANSI_BLACK ANSI_CSI "30m"
+#define ANSI_RED ANSI_CSI "31m"
+#define ANSI_GREEN ANSI_CSI "32m"
+#define ANSI_YELLOW ANSI_CSI "33m"
+#define ANSI_BLUE ANSI_CSI "34m"
+#define ANSI_MAGENTA ANSI_CSI "35m"
+#define ANSI_CYAN ANSI_CSI "36m"
+#define ANSI_WHITE ANSI_CSI "37m"
+
+#define ANSI_BRIGHT_BLACK ANSI_CSI "90m"
+#define ANSI_BRIGHT_RED ANSI_CSI "91m"
+#define ANSI_BRIGHT_GREEN ANSI_CSI "92m"
+#define ANSI_BRIGHT_YELLOW ANSI_CSI "93m"
+#define ANSI_BRIGHT_BLUE ANSI_CSI "94m"
+#define ANSI_BRIGHT_MAGENTA ANSI_CSI "95m"
+#define ANSI_BRIGHT_CYAN ANSI_CSI "96m"
+#define ANSI_BRIGHT_WHITE ANSI_CSI "97m"
diff --git a/include/subsys.h b/include/subsys.h
new file mode 100644
index 0000000..1515293
--- /dev/null
+++ b/include/subsys.h
@@ -0,0 +1,7 @@
+#ifndef __SUBSYS_H
+#define __SUBSYS_H
+
+int __impl_start_subsystem(char *name, int (*fn)(void));
+#define start_subsystem(fn) __impl_start_subsystem(#fn, fn)
+
+#endif
diff --git a/include/util.h b/include/util.h
new file mode 100644
index 0000000..97435be
--- /dev/null
+++ b/include/util.h
@@ -0,0 +1,5 @@
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+void die(const char *fmt, ...);
+
+#define writeputs(str) write(STDOUT_FILENO, str, strlen(str));
diff --git a/init/init.c b/init/init.c
new file mode 100644
index 0000000..5363082
--- /dev/null
+++ b/init/init.c
@@ -0,0 +1,60 @@
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <init.h>
+#include <util.h>
+
+/* For some reason, I get SIGSEGV'd when running because a random-ass
+ byte was inserted where it isnt supposed to be. Added a safety byte
+ because I cannot be asked to try to figure out how to do this cleanly. */
+static unsigned long __1bsafebuf
+ __attribute__((used)) __attribute__((section(".1bsafebuf.init"))) = 0;
+
+extern initcall_entry_t __initcall1_start[];
+extern initcall_entry_t __initcall2_start[];
+extern initcall_entry_t __initcall3_start[];
+extern initcall_entry_t __initcall4_start[];
+extern initcall_entry_t __initcall5_start[];
+extern initcall_entry_t __initcall_end[];
+
+static initcall_entry_t *initcall_levels[] = {
+ __initcall1_start,
+ __initcall2_start,
+ __initcall3_start,
+ __initcall4_start,
+ __initcall5_start,
+ __initcall_end,
+};
+
+static void do_initcall_level(int level)
+{
+ initcall_entry_t *fn;
+
+ for (fn = initcall_levels[level - 1]; fn < initcall_levels[level]; fn++)
+ initcall_from_entry(fn)();
+}
+
+static void do_initcalls(void)
+{
+ unsigned long level;
+ for (level = 1; level < ARRAY_SIZE(initcall_levels); level++) {
+ do_initcall_level(level);
+ }
+}
+
+int main(void)
+{
+ do_initcalls();
+
+ static sigset_t set;
+ sigaddset(&set, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+
+ while(1) {
+ int sig;
+ sigwait(&set, &sig);
+ if(sig == SIGCHLD)
+ while(waitpid(0, NULL, WNOHANG) > 0)
+ ;
+ }
+}
diff --git a/init/log.c b/init/log.c
new file mode 100644
index 0000000..3cdaa06
--- /dev/null
+++ b/init/log.c
@@ -0,0 +1,69 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+#include <unistd.h>
+
+#include <log.h>
+#include <util.h>
+
+static const char *colors[] = {
+ [0] = ANSI_BLINK ANSI_REVERSE ANSI_BOLD ANSI_RED,
+ [1] = ANSI_REVERSE ANSI_BOLD ANSI_RED,
+ [2] = ANSI_BOLD ANSI_RED,
+ [3] = ANSI_RED,
+ [4] = ANSI_BOLD,
+ [5] = ANSI_BRIGHT_WHITE,
+ [6] = ANSI_RESET,
+ [7] = ANSI_ITALIC ANSI_BRIGHT_BLUE,
+};
+
+int console_lock = 0;
+
+int print(const char *fmt, ...)
+{
+ int loglevel = 5;
+ if(fmt[0] == LOG_SOH_ASCII) {
+ loglevel = (fmt[1] - 0x30) % 10;
+ fmt += 2;
+ }
+
+ char buf[512];
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, 512, fmt, ap);
+ va_end(ap);
+ buf[512 - 1] = '\0';
+
+ size_t colon = 0;
+ for(; colon < strlen(buf); ++colon) {
+ if(buf[colon] == ':')
+ break;
+ }
+
+ /* spin lock, at the cost of architecture portability */
+ __asm__(".spin_lock:");
+ __asm__("mov rax, 1");
+ __asm__("xchg rax, [console_lock]");
+ __asm__("test rax, rax");
+ __asm__("jnz .spin_lock");
+
+ if(buf[colon] == ':') {
+ writeputs(ANSI_RESET);
+ writeputs(colors[loglevel]);
+ writeputs(ANSI_YELLOW);
+ write(STDOUT_FILENO, buf, colon);
+ writeputs(ANSI_RESET);
+ }
+ writeputs(colors[loglevel]);
+ if(colon && *(buf + colon)) {
+ writeputs(buf + colon);
+ } else {
+ writeputs(buf);
+ }
+ writeputs(ANSI_RESET);
+
+ console_lock = 0;
+ return 0;
+}
diff --git a/init/subsys.c b/init/subsys.c
new file mode 100644
index 0000000..0435f1a
--- /dev/null
+++ b/init/subsys.c
@@ -0,0 +1,55 @@
+#include <errno.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <config.h>
+#include <log.h>
+#include <util.h>
+
+#define STACK_SIZE 8192 * 512
+
+struct subsystem_info {
+ char *fn_name;
+ int (*fn)(void);
+};
+
+int __subsystem_entry(struct subsystem_info *info)
+{
+
+ char *name = malloc(16 * sizeof(char));
+ snprintf(name, 16, NAME_SHORTHAND ": %s", info->fn_name);
+ name[15] = '\0';
+ prctl(PR_SET_NAME, name);
+ free(name);
+
+ int ret = info->fn();
+
+ free(info);
+
+ return ret;
+}
+
+int __impl_start_subsystem(char *fn_name, int (*fn)(void))
+{
+ void *stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_STACK | MAP_PRIVATE, -1, 0);
+ if((long)stack < 0)
+ die("subsys mmap:");
+
+ struct subsystem_info *info = malloc(sizeof(struct subsystem_info));
+ info->fn_name = fn_name;
+ info->fn = fn;
+
+ int pid = clone((int (*)(void *))__subsystem_entry, stack, CLONE_FILES | CLONE_VM, info);
+ if(pid < 0) {
+ munmap(stack, STACK_SIZE);
+ die("subsys clone:");
+ }
+
+ return 0;
+}
diff --git a/init/util.c b/init/util.c
new file mode 100644
index 0000000..1a08234
--- /dev/null
+++ b/init/util.c
@@ -0,0 +1,22 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if(fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ exit(1);
+}
diff --git a/linker.ld b/linker.ld
new file mode 100644
index 0000000..71e8b89
--- /dev/null
+++ b/linker.ld
@@ -0,0 +1,251 @@
+/* Default linker script, for normal executables */
+/* Copyright (C) 2014-2023 Free Software Foundation, Inc.
+ Copying and distribution of this script, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. */
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
+ "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64"); SEARCH_DIR("/usr/lib64/binutils/x86_64-pc-linux-gnu/2.4064"); SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib"); SEARCH_DIR("/usr/lib64/binutils/x86_64-pc-linux-gnu/2.40"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .note.gnu.build-id : { *(.note.gnu.build-id) }
+ .hash : { *(.hash) }
+ .gnu.hash : { *(.gnu.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.init : { *(.rela.init) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rela.data.rel.ro : { *(.rela.data.rel.ro .rela.data.rel.ro.* .rela.gnu.linkonce.d.rel.ro.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.got : { *(.rela.got) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rela.ldata : { *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) }
+ .rela.lbss : { *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) }
+ .rela.lrodata : { *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) }
+ .rela.ifunc : { *(.rela.ifunc) }
+ .rela.plt :
+ {
+ *(.rela.plt)
+ PROVIDE_HIDDEN (__rela_iplt_start = .);
+ *(.rela.iplt)
+ PROVIDE_HIDDEN (__rela_iplt_end = .);
+ }
+ .relr.dyn : { *(.relr.dyn) }
+ .init :
+ {
+ KEEP (*(SORT_NONE(.init)))
+ }
+ .plt : { *(.plt) *(.iplt) }
+.plt.got : { *(.plt.got) }
+.plt.sec : { *(.plt.sec) }
+ .text :
+ {
+ *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+ *(.text.exit .text.exit.*)
+ *(.text.startup .text.startup.*)
+ *(.text.hot .text.hot.*)
+ *(SORT(.text.sorted.*))
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf.em. */
+ *(.gnu.warning)
+
+ . = ALIGN(8);
+ KEEP(*(.1bsafebuf.init))
+ __initcall1_start = .; KEEP(*(.initcall1.init))
+ __initcall2_start = .; KEEP(*(.initcall2.init))
+ __initcall3_start = .; KEEP(*(.initcall3.init))
+ __initcall4_start = .; KEEP(*(.initcall4.init))
+ __initcall5_start = .; KEEP(*(.initcall5.init))
+ __initcall_end = .;
+ }
+ .fini :
+ {
+ KEEP (*(SORT_NONE(.fini)))
+ }
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+ .sframe : ONLY_IF_RO { *(.sframe) *(.sframe.*) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
+ /* These sections are generated by the Sun/Oracle C++ compiler. */
+ .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+ .sframe : ONLY_IF_RW { *(.sframe) *(.sframe.*) }
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) }
+ /* Thread Local Storage sections */
+ .tdata :
+ {
+ PROVIDE_HIDDEN (__tdata_start = .);
+ *(.tdata .tdata.* .gnu.linkonce.td.*)
+ }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .preinit_array :
+ {
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+ }
+ .init_array :
+ {
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+ KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ }
+ .fini_array :
+ {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+ KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*crtbegin?.o(.ctors))
+ /* We don't want to include the .ctor section from
+ the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*crtbegin?.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
+ .dynamic : { *(.dynamic) }
+ .got : { *(.got) *(.igot) }
+ . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
+ .got.plt : { *(.got.plt) *(.igot.plt) }
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ _edata = .; PROVIDE (edata = .);
+ . = .;
+ __bss_start = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections.
+ FIXME: Why do we need it? When there is no .bss section, we do not
+ pad the .data section. */
+ . = ALIGN(. != 0 ? 64 / 8 : 1);
+ }
+ .lbss :
+ {
+ *(.dynlbss)
+ *(.lbss .lbss.* .gnu.linkonce.lb.*)
+ *(LARGE_COMMON)
+ }
+ . = ALIGN(64 / 8);
+ . = SEGMENT_START("ldata-segment", .);
+ .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+ {
+ *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
+ }
+ .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+ {
+ *(.ldata .ldata.* .gnu.linkonce.l.*)
+ . = ALIGN(. != 0 ? 64 / 8 : 1);
+ }
+ . = ALIGN(64 / 8);
+ _end = .; PROVIDE (end = .);
+ . = DATA_SEGMENT_END (.);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1. */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions. */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2. */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2. */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions. */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* DWARF 3. */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ /* DWARF 5. */
+ .debug_addr 0 : { *(.debug_addr) }
+ .debug_line_str 0 : { *(.debug_line_str) }
+ .debug_loclists 0 : { *(.debug_loclists) }
+ .debug_macro 0 : { *(.debug_macro) }
+ .debug_names 0 : { *(.debug_names) }
+ .debug_rnglists 0 : { *(.debug_rnglists) }
+ .debug_str_offsets 0 : { *(.debug_str_offsets) }
+ .debug_sup 0 : { *(.debug_sup) }
+ .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
+}
diff --git a/net/net.c b/net/net.c
new file mode 100644
index 0000000..11c66ff
--- /dev/null
+++ b/net/net.c
@@ -0,0 +1,2 @@
+#include <subsys.h>
+