diff --git a/sys/ld/arch/x86_64/resolve.S b/sys/ld/arch/x86_64/resolve.S new file mode 100644 index 0000000..7e4a208 --- /dev/null +++ b/sys/ld/arch/x86_64/resolve.S @@ -0,0 +1,47 @@ +.code64 + +.global _dl_runtime_resolve +.type _dl_runtime_resolve, @function + +.extern dl_runtime_resolve +.type dl_runtime_resolve, @function + +_dl_runtime_resolve: + // pop %rdi + // pop %rsi + pop %rax + pop %r11 + + push %rsp + push %rbp + push %rdi + push %rsi + push %rdx + push %rcx + push %r8 + push %r9 + push %rbx + push %r12 + push %r13 + push %r14 + push %r15 + + mov %rax, %rdi + mov %r11, %rsi + call dl_runtime_resolve + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbx + pop %r9 + pop %r8 + pop %rcx + pop %rdx + pop %rsi + pop %rdi + pop %rbp + pop %rsp + + jmp *%rax diff --git a/sys/ld/btree.c b/sys/ld/btree.c new file mode 100644 index 0000000..d999e96 --- /dev/null +++ b/sys/ld/btree.c @@ -0,0 +1,691 @@ +/* + The Clear BSD License + + Copyright (c) 2023 Max Wash + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted (subject to the limitations in the disclaimer + below) provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + */ + +/* templated AVL binary tree implementation + + this file implements an extensible AVL binary tree data structure. + + the primary rule of an AVL binary tree is that for a given node N, + the heights of N's left and right subtrees can differ by at most 1. + + the height of a subtree is the length of the longest path between + the root of the subtree and a leaf node, including the root node itself. + + the height of a leaf node is 1. + + when a node is inserted into or deleted from the tree, this rule may + be broken, in which the tree must be rotated to restore the balance. + + no more than one rotation is required for any insert operations, + while multiple rotations may be required for a delete operation. + + there are four types of rotations that can be applied to a tree: + - left rotation + - right rotation + - double left rotations + - double right rotations + + by enforcing the balance rule, for a tree with n nodes, the worst-case + performance for insert, delete, and search operations is guaranteed + to be O(log n). + + this file intentionally excludes any kind of search function implementation. + it is up to the programmer to implement their own tree node type + using struct btree_node, and their own search function using struct btree. + this allows the programmer to define their own node types with complex + non-integer key types. btree.h contains a number of macros to help + define these functions. the macros do all the work, you just have to + provide a comparator function. +*/ + +#include "btree.h" + +#include + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define IS_LEFT_CHILD(p, c) ((p) && (c) && ((p)->b_left == (c))) +#define IS_RIGHT_CHILD(p, c) ((p) && (c) && ((p)->b_right == (c))) + +#define HAS_LEFT_CHILD(x) ((x) && ((x)->b_left)) +#define HAS_RIGHT_CHILD(x) ((x) && ((x)->b_right)) + +#define HAS_NO_CHILDREN(x) ((x) && (!(x)->b_left) && (!(x)->b_right)) +#define HAS_ONE_CHILD(x) \ + ((HAS_LEFT_CHILD(x) && !HAS_RIGHT_CHILD(x)) \ + || (!HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x))) +#define HAS_TWO_CHILDREN(x) (HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x)) + +#define HEIGHT(x) ((x) ? (x)->b_height : 0) + +static inline void update_height(struct btree_node *x) +{ + x->b_height = MAX(HEIGHT(x->b_left), HEIGHT((x->b_right))) + 1; +} + +static inline int bf(struct btree_node *x) +{ + int bf = 0; + + if (!x) { + return bf; + } + + if (x->b_right) { + bf += x->b_right->b_height; + } + + if (x->b_left) { + bf -= x->b_left->b_height; + } + + return bf; +} + +/* perform a left rotation on a subtree + + if you have a tree like this: + + Z + / \ + X . + / \ + . Y + / \ + . . + + and you perform a left rotation on node X, + you will get the following tree: + + Z + / \ + Y . + / \ + X . + / \ + . . + + note that this function does NOT update b_height for the rotated + nodes. it is up to you to call update_height_to_root(). +*/ +static void rotate_left(struct btree *tree, struct btree_node *x) +{ + struct btree_node *y = x->b_right; + + struct btree_node *p = x->b_parent; + + if (y->b_left) { + y->b_left->b_parent = x; + } + + x->b_right = y->b_left; + + if (!p) { + tree->b_root = y; + } else if (x == p->b_left) { + p->b_left = y; + } else { + p->b_right = y; + } + + x->b_parent = y; + y->b_left = x; + y->b_parent = p; +} + +static void update_height_to_root(struct btree_node *x) +{ + while (x) { + update_height(x); + x = x->b_parent; + } +} + +/* perform a right rotation on a subtree + + if you have a tree like this: + + Z + / \ + . X + / \ + Y . + / \ + . . + + and you perform a right rotation on node X, + you will get the following tree: + + Z + / \ + . Y + / \ + . X + / \ + . . + + note that this function does NOT update b_height for the rotated + nodes. it is up to you to call update_height_to_root(). +*/ +static void rotate_right(struct btree *tree, struct btree_node *y) +{ + struct btree_node *x = y->b_left; + + struct btree_node *p = y->b_parent; + + if (x->b_right) { + x->b_right->b_parent = y; + } + + y->b_left = x->b_right; + + if (!p) { + tree->b_root = x; + } else if (y == p->b_left) { + p->b_left = x; + } else { + p->b_right = x; + } + + y->b_parent = x; + x->b_right = y; + x->b_parent = p; +} + +/* for a given node Z, perform a right rotation on Z's right child, + followed by a left rotation on Z itself. + + if you have a tree like this: + + Z + / \ + . X + / \ + Y . + / \ + . . + + and you perform a double-left rotation on node Z, + you will get the following tree: + + Y + / \ + / \ + Z X + / \ / \ + . . . . + + note that, unlike rotate_left and rotate_right, this function + DOES update b_height for the rotated nodes (since it needs to be + done in a certain order). +*/ +static void rotate_double_left(struct btree *tree, struct btree_node *z) +{ + struct btree_node *x = z->b_right; + struct btree_node *y = x->b_left; + + rotate_right(tree, x); + rotate_left(tree, z); + + update_height(z); + update_height(x); + + while (y) { + update_height(y); + y = y->b_parent; + } +} + +/* for a given node Z, perform a left rotation on Z's left child, + followed by a right rotation on Z itself. + + if you have a tree like this: + + Z + / \ + X . + / \ + . Y + / \ + . . + + and you perform a double-right rotation on node Z, + you will get the following tree: + + Y + / \ + / \ + X Z + / \ / \ + . . . . + + note that, unlike rotate_left and rotate_right, this function + DOES update b_height for the rotated nodes (since it needs to be + done in a certain order). +*/ +static void rotate_double_right(struct btree *tree, struct btree_node *z) +{ + struct btree_node *x = z->b_left; + struct btree_node *y = x->b_right; + + rotate_left(tree, x); + rotate_right(tree, z); + + update_height(z); + update_height(x); + + while (y) { + update_height(y); + y = y->b_parent; + } +} + +/* run after an insert operation. checks that the balance factor + of the local subtree is within the range -1 <= BF <= 1. if it + is not, rotate the subtree to restore balance. + + note that at most one rotation should be required after a node + is inserted into the tree. + + this function depends on all nodes in the tree having + correct b_height values. + + @param w the node that was just inserted into the tree +*/ +static void insert_fixup(struct btree *tree, struct btree_node *w) +{ + struct btree_node *z = NULL, *y = NULL, *x = NULL; + + z = w; + while (z) { + if (bf(z) >= -1 && bf(z) <= 1) { + goto next_ancestor; + } + + if (IS_LEFT_CHILD(z, y)) { + if (IS_LEFT_CHILD(y, x)) { + rotate_right(tree, z); + update_height_to_root(z); + } else { + rotate_double_right(tree, z); + } + } else { + if (IS_LEFT_CHILD(y, x)) { + rotate_double_left(tree, z); + } else { + rotate_left(tree, z); + update_height_to_root(z); + } + } + + next_ancestor: + x = y; + y = z; + z = z->b_parent; + } +} + +/* run after a delete operation. checks that the balance factor + of the local subtree is within the range -1 <= BF <= 1. if it + is not, rotate the subtree to restore balance. + + note that, unlike insert_fixup, multiple rotations may be required + to restore balance after a node is deleted. + + this function depends on all nodes in the tree having + correct b_height values. + + @param w one of the following: + - the parent of the node that was deleted if the node + had no children. + - the parent of the node that replaced the deleted node + if the deleted node had two children. + - the node that replaced the node that was deleted, if + the node that was deleted had one child. +*/ +static void delete_fixup(struct btree *tree, struct btree_node *w) +{ + struct btree_node *z = w; + + while (z) { + if (bf(z) > 1) { + if (bf(z->b_right) >= 0) { + rotate_left(tree, z); + update_height_to_root(z); + } else { + rotate_double_left(tree, z); + } + } else if (bf(z) < -1) { + if (bf(z->b_left) <= 0) { + rotate_right(tree, z); + update_height_to_root(z); + } else { + rotate_double_right(tree, z); + } + } + + z = z->b_parent; + } +} + +/* updates b_height for all nodes between the inserted node and the root + of the tree, and calls insert_fixup. + + @param node the node that was just inserted into the tree. +*/ +void btree_insert_fixup(struct btree *tree, struct btree_node *node) +{ + node->b_height = 0; + + struct btree_node *cur = node; + while (cur) { + update_height(cur); + cur = cur->b_parent; + } + + insert_fixup(tree, node); +} + +/* remove a node from a tree. + + this function assumes that `node` has no children, and therefore + doesn't need to be replaced. + + updates b_height for all nodes between `node` and the tree root. + + @param node the node to delete. +*/ +static struct btree_node *remove_node_with_no_children( + struct btree *tree, + struct btree_node *node) +{ + struct btree_node *w = node->b_parent; + struct btree_node *p = node->b_parent; + node->b_parent = NULL; + + if (!p) { + tree->b_root = NULL; + } else if (IS_LEFT_CHILD(p, node)) { + p->b_left = NULL; + } else { + p->b_right = NULL; + } + + while (p) { + update_height(p); + p = p->b_parent; + } + + return w; +} + +/* remove a node from a tree. + + this function assumes that `node` has one child. + the child of `node` is inherited by `node`'s parent, and `node` is removed. + + updates b_height for all nodes between the node that replaced + `node` and the tree root. + + @param node the node to delete. +*/ +static struct btree_node *replace_node_with_one_subtree( + struct btree *tree, + struct btree_node *node) +{ + struct btree_node *p = node->b_parent; + struct btree_node *z = NULL; + + if (HAS_LEFT_CHILD(node)) { + z = node->b_left; + } else { + z = node->b_right; + } + + struct btree_node *w = z; + if (!p) { + tree->b_root = z; + } else if (IS_LEFT_CHILD(p, node)) { + p->b_left = z; + } else if (IS_RIGHT_CHILD(p, node)) { + p->b_right = z; + } + + z->b_parent = p; + + node->b_parent = NULL; + node->b_left = node->b_right = NULL; + + while (z) { + update_height(z); + z = z->b_parent; + } + + return w; +} + +/* remove a node from a tree. + + this function assumes that `node` has two children. + find the in-order successor Y of `node` (the largest node in `node`'s left + sub-tree), removes `node` from the tree and moves Y to where `node` used to + be. + + if Y has a child (it will never have more than one), have Y's parent inherit + Y's child. + + updates b_height for all nodes between the deepest node that was modified + and the tree root. + + @param z the node to delete. +*/ +static struct btree_node *replace_node_with_two_subtrees( + struct btree *tree, + struct btree_node *z) +{ + /* x will replace z */ + struct btree_node *x = z->b_left; + + while (x->b_right) { + x = x->b_right; + } + + /* y is the node that will replace x (if x has a left child) */ + struct btree_node *y = x->b_left; + + /* w is the starting point for the height update and fixup */ + struct btree_node *w = x; + if (w->b_parent != z) { + w = w->b_parent; + } + + if (y) { + w = y; + } + + if (IS_LEFT_CHILD(x->b_parent, x)) { + x->b_parent->b_left = y; + } else if (IS_RIGHT_CHILD(x->b_parent, x)) { + x->b_parent->b_right = y; + } + + if (y) { + y->b_parent = x->b_parent; + } + + if (IS_LEFT_CHILD(z->b_parent, z)) { + z->b_parent->b_left = x; + } else if (IS_RIGHT_CHILD(z->b_parent, z)) { + z->b_parent->b_right = x; + } + + x->b_parent = z->b_parent; + x->b_left = z->b_left; + x->b_right = z->b_right; + + if (x->b_left) { + x->b_left->b_parent = x; + } + + if (x->b_right) { + x->b_right->b_parent = x; + } + + if (!x->b_parent) { + tree->b_root = x; + } + + struct btree_node *cur = w; + while (cur) { + update_height(cur); + cur = cur->b_parent; + } + + return w; +} + +/* delete a node from the tree and re-balance it afterwards */ +void btree_delete(struct btree *tree, struct btree_node *node) +{ + struct btree_node *w = NULL; + + if (HAS_NO_CHILDREN(node)) { + w = remove_node_with_no_children(tree, node); + } else if (HAS_ONE_CHILD(node)) { + w = replace_node_with_one_subtree(tree, node); + } else if (HAS_TWO_CHILDREN(node)) { + w = replace_node_with_two_subtrees(tree, node); + } + + if (w) { + delete_fixup(tree, w); + } + + node->b_left = node->b_right = node->b_parent = NULL; +} + +struct btree_node *btree_first(struct btree *tree) +{ + /* the first node in the tree is the node with the smallest key. + we keep moving left until we can't go any further */ + struct btree_node *cur = tree->b_root; + if (!cur) { + return NULL; + } + + while (cur->b_left) { + cur = cur->b_left; + } + + return cur; +} + +struct btree_node *btree_last(struct btree *tree) +{ + /* the first node in the tree is the node with the largest key. + we keep moving right until we can't go any further */ + struct btree_node *cur = tree->b_root; + if (!cur) { + return NULL; + } + + while (cur->b_right) { + cur = cur->b_right; + } + + return cur; +} + +struct btree_node *btree_next(struct btree_node *node) +{ + if (!node) { + return NULL; + } + + /* there are two possibilities for the next node: + + 1. if `node` has a right sub-tree, every node in this sub-tree is + bigger than node. the in-order successor of `node` is the smallest + node in this subtree. + 2. if `node` has no right sub-tree, we've reached the largest node in + the sub-tree rooted at `node`. we need to go back to our parent + and continue the search elsewhere. + */ + if (node->b_right) { + /* case 1: step into `node`'s right sub-tree and keep going + left to find the smallest node */ + struct btree_node *cur = node->b_right; + while (cur->b_left) { + cur = cur->b_left; + } + + return cur; + } + + /* case 2: keep stepping back up towards the root of the tree. + if we encounter a step where we are our parent's left child, + we've found a parent with a value larger than us. this parent + is the in-order successor of `node` */ + while (node->b_parent && node->b_parent->b_left != node) { + node = node->b_parent; + } + + return node->b_parent; +} + +struct btree_node *btree_prev(struct btree_node *node) +{ + if (!node) { + return NULL; + } + + /* there are two possibilities for the previous node: + + 1. if `node` has a left sub-tree, every node in this sub-tree is + smaller than `node`. the in-order predecessor of `node` is the + largest node in this subtree. + 2. if `node` has no left sub-tree, we've reached the smallest node in + the sub-tree rooted at `node`. we need to go back to our parent + and continue the search elsewhere. + */ + if (node->b_left) { + /* case 1: step into `node`'s left sub-tree and keep going + right to find the largest node */ + struct btree_node *cur = node->b_left; + while (cur->b_right) { + cur = cur->b_right; + } + + return cur; + } + + /* case 2: keep stepping back up towards the root of the tree. + if we encounter a step where we are our parent's right child, + we've found a parent with a value smaller than us. this parent + is the in-order predecessor of `node`. */ + while (node->b_parent && node->b_parent->b_right != node) { + node = node->b_parent; + } + + return node->b_parent; +} diff --git a/sys/ld/btree.h b/sys/ld/btree.h new file mode 100644 index 0000000..aee1c20 --- /dev/null +++ b/sys/ld/btree.h @@ -0,0 +1,475 @@ +/* + The Clear BSD License + + Copyright (c) 2023 Max Wash + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted (subject to the limitations in the disclaimer + below) provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + */ + +#ifndef BTREE_H_ +#define BTREE_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* if your custom structure contains a struct btree_node (i.e. it can be part of + a btree), you can use this macro to convert a struct btree_node* to a + your_type* + + @param t the name of your custom type (something that can be passed to + offsetof) + @param m the name of the struct btree_node member variable within your custom + type. + @param v the struct btree_node pointer that you wish to convert. if this is + NULL, NULL will be returned. +*/ +#define BTREE_CONTAINER(t, m, v) \ + ((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0)) + +/* defines a simple node insertion function. + this function assumes that your nodes have simple integer keys that can be + compared with the usual operators. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + int key; + struct btree_node base; + } + + You would use the following call to generate an insert function for a tree + with this node type: + + BTREE_DEFINE_SIMPLE_INSERT(struct my_tree_node, base, key, + my_tree_node_insert); + + Which would emit a function defined like: + + static void my_tree_node_insert(struct btree *tree, struct my_tree_node + *node); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. +*/ +#define BTREE_DEFINE_SIMPLE_INSERT( \ + node_type, \ + container_node_member, \ + container_key_member, \ + function_name) \ + void function_name(struct btree *tree, node_type *node) \ + { \ + if (!tree->b_root) { \ + tree->b_root = &node->container_node_member; \ + btree_insert_fixup( \ + tree, \ + &node->container_node_member); \ + return; \ + } \ + \ + struct btree_node *cur = tree->b_root; \ + while (1) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + struct btree_node *next = NULL; \ + \ + if (node->container_key_member \ + > cur_node->container_key_member) { \ + next = btree_right(cur); \ + \ + if (!next) { \ + btree_put_right( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else if ( \ + node->container_key_member \ + < cur_node->container_key_member) { \ + next = btree_left(cur); \ + \ + if (!next) { \ + btree_put_left( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else { \ + return; \ + } \ + \ + cur = next; \ + } \ + \ + btree_insert_fixup(tree, &node->container_node_member); \ + } + +/* defines a node insertion function. + this function should be used for trees with complex node keys that cannot be + directly compared. a comparator for your keys must be supplied. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + complex_key_t key; + struct btree_node base; + } + + You would need to define a comparator function or macro with the following + signature: + + int my_comparator(struct my_tree_node *a, struct my_tree_node *b); + + Which implements the following: + + return -1 if a < b + return 0 if a == b + return 1 if a > b + + You would use the following call to generate an insert function for a tree + with this node type: + + BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert, + my_comparator); + + Which would emit a function defined like: + + static void my_tree_node_insert(struct btree *tree, struct my_tree_node + *node); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. + @param comparator the name of a comparator function or functional-macro that + conforms to the requirements listed above. +*/ +#define BTREE_DEFINE_INSERT( \ + node_type, \ + container_node_member, \ + container_key_member, \ + function_name, \ + comparator) \ + void function_name(struct btree *tree, node_type *node) \ + { \ + if (!tree->b_root) { \ + tree->b_root = &node->container_node_member; \ + btree_insert_fixup( \ + tree, \ + &node->container_node_member); \ + return; \ + } \ + \ + struct btree_node *cur = tree->b_root; \ + while (1) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + struct btree_node *next = NULL; \ + int cmp = comparator(node, cur_node); \ + \ + if (cmp == 1) { \ + next = btree_right(cur); \ + \ + if (!next) { \ + btree_put_right( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else if (cmp == -1) { \ + next = btree_left(cur); \ + \ + if (!next) { \ + btree_put_left( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else { \ + return; \ + } \ + \ + cur = next; \ + } \ + \ + btree_insert_fixup(tree, &node->container_node_member); \ + } + +/* defines a simple tree search function. + this function assumes that your nodes have simple integer keys that can be + compared with the usual operators. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + int key; + struct btree_node base; + } + + You would use the following call to generate a search function for a tree + with this node type: + + BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key, + my_tree_node_get); + + Which would emit a function defined like: + + static struct my_tree_node *my_tree_node_get(struct btree *tree, int key); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param key_type the type name of the key embedded in your custom tree node + type. this type must be compatible with the builtin comparison operators. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. +*/ +#define BTREE_DEFINE_SIMPLE_GET( \ + node_type, \ + key_type, \ + container_node_member, \ + container_key_member, \ + function_name) \ + node_type *function_name(struct btree *tree, key_type key) \ + { \ + struct btree_node *cur = tree->b_root; \ + while (cur) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + if (key > cur_node->container_key_member) { \ + cur = btree_right(cur); \ + } else if (key < cur_node->container_key_member) { \ + cur = btree_left(cur); \ + } else { \ + return cur_node; \ + } \ + } \ + \ + return NULL; \ + } + +/* perform an in-order traversal of a binary tree + + If you have a tree defined like: + + struct btree my_tree; + + with nodes defined like: + + struct my_tree_node { + int key; + struct btree_node base; + } + + and you want to do something like: + + foreach (struct my_tree_node *node : my_tree) { ... } + + you should use this: + + btree_foreach (struct my_tree_node, node, &my_tree, base) { ... } + + @param iter_type the type name of the iterator variable. this should be the + tree's node type, and shouldn't be a pointer. + @param iter_name the name of the iterator variable. + @param tree_name a pointer to the tree to traverse. + @param node_member the name of the struct btree_node member variable within + the tree node type. +*/ +#define btree_foreach(iter_type, iter_name, tree_name, node_member) \ + for (iter_type *iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_first(tree_name)); \ + iter_name; \ + iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_next(&((iter_name)->node_member)))) + +/* perform an reverse in-order traversal of a binary tree + + If you have a tree defined like: + + struct btree my_tree; + + with nodes defined like: + + struct my_tree_node { + int key; + struct btree_node base; + } + + and you want to do something like: + + foreach (struct my_tree_node *node : reverse(my_tree)) { ... } + + you should use this: + + btree_foreach_r (struct my_tree_node, node, &my_tree, base) { ... } + + @param iter_type the type name of the iterator variable. this should be the + tree's node type, and shouldn't be a pointer. + @param iter_name the name of the iterator variable. + @param tree_name a pointer to the tree to traverse. + @param node_member the name of the struct btree_node member variable within + the tree node type. +*/ +#define btree_foreach_r(iter_type, iter_name, tree_name, node_member) \ + for (iter_type *iter_name \ + = BTREE_CONTAINER(iter_type, node_member, btree_last(tree_name)); \ + iter_name; \ + iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_prev(&((iter_name)->node_member)))) + +/* binary tree nodes. this *cannot* be used directly. you need to define a + custom node type that contains a member variable of type struct btree_node. + + you would then use the supplied macros to define functions to manipulate your + custom binary tree. +*/ +struct btree_node { + struct btree_node *b_parent, *b_left, *b_right; + unsigned short b_height; +}; + +/* binary tree. unlike struct btree_node, you can define variables of type + * struct btree. */ +struct btree { + struct btree_node *b_root; +}; + +/* re-balance a binary tree after an insertion operation. + + NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or + similar, this function will automatically called for you. + + @param tree the tree to re-balance. + @param node the node that was just inserted into the tree. +*/ +extern void btree_insert_fixup(struct btree *tree, struct btree_node *node); + +/* delete a node from a binary tree and re-balance the tree afterwards. + + @param tree the tree to delete from + @param node the node to delete. +*/ +extern void btree_delete(struct btree *tree, struct btree_node *node); + +/* get the first node in a binary tree. + + this will be the node with the smallest key (i.e. the node that is + furthest-left from the root) +*/ +extern struct btree_node *btree_first(struct btree *tree); + +/* get the last node in a binary tree. + + this will be the node with the largest key (i.e. the node that is + furthest-right from the root) +*/ +extern struct btree_node *btree_last(struct btree *tree); +/* for any binary tree node, this function returns the node with the + * next-largest key value */ +extern struct btree_node *btree_next(struct btree_node *node); +/* for any binary tree node, this function returns the node with the + * next-smallest key value */ +extern struct btree_node *btree_prev(struct btree_node *node); + +static inline bool btree_empty(const struct btree *tree) +{ + return tree->b_root == NULL; +} + +/* sets `child` as the immediate left-child of `parent` */ +static inline void btree_put_left( + struct btree_node *parent, + struct btree_node *child) +{ + parent->b_left = child; + child->b_parent = parent; +} + +/* sets `child` as the immediate right-child of `parent` */ +static inline void btree_put_right( + struct btree_node *parent, + struct btree_node *child) +{ + parent->b_right = child; + child->b_parent = parent; +} + +/* get the immediate left-child of `node` */ +static inline struct btree_node *btree_left(struct btree_node *node) +{ + return node->b_left; +} + +/* get the immediate right-child of `node` */ +static inline struct btree_node *btree_right(struct btree_node *node) +{ + return node->b_right; +} + +/* get the immediate parent of `node` */ +static inline struct btree_node *btree_parent(struct btree_node *node) +{ + return node->b_parent; +} + +/* get the height of `node`. + + the height of a node is defined as the length of the longest path + between the node and a leaf node. + + this count includes the node itself, so the height of a leaf node will be 1. +*/ +static inline unsigned short btree_height(struct btree_node *node) +{ + return node->b_height; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sys/ld/elf.c b/sys/ld/elf.c new file mode 100644 index 0000000..353d2eb --- /dev/null +++ b/sys/ld/elf.c @@ -0,0 +1,800 @@ +#include "elf.h" + +#include "resolve.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define NEEDS_NOTHING 0 +#define NEEDS_VDSO 1 +#define NEEDS_MORE 2 + +#define ACL (PF_R | PF_W | PF_X) +#define ACCESS(x) ((x) & ACL) + +/* TODO in case we ever support ELF32 images */ +#define elf_class_bits(x) (64) + +#define PAGE_SIZE (image->e_page_size) +#define PAGE_MASK (image->e_page_size - 1) +#define PAGE_OFFSET(v) ((v) & (PAGE_SIZE - 1)) +#define PAGE_ALIGN_DOWN(v) (v) &= ~(PAGE_SIZE - 1) +#define PAGE_ALIGN_UP(v) \ + do { \ + if ((v) & (PAGE_SIZE - 1)) { \ + v &= ~(PAGE_SIZE - 1); \ + v += PAGE_SIZE; \ + } \ + } while (0) + +#undef DEBUG_LOG + +const char *elf_image_status_to_string(enum elf_image_status status) +{ +#define ENUM_STR(s) \ + case s: \ + return #s + switch (status) { + ENUM_STR(ELF_IMAGE_NONE); + ENUM_STR(ELF_IMAGE_OPEN); + ENUM_STR(ELF_IMAGE_PARSED); + ENUM_STR(ELF_IMAGE_LOADED); + ENUM_STR(ELF_IMAGE_LINKED); + default: + return "UNKNOWN"; + } +#undef ENUM_STR +} + +static bool elf_validate_ehdr(elf_ehdr_t *hdr) +{ + if (hdr->e_ident[EI_MAG0] != ELF_MAG0) { + return false; + } + + if (hdr->e_ident[EI_MAG1] != ELF_MAG1) { + return false; + } + + if (hdr->e_ident[EI_MAG2] != ELF_MAG2) { + return false; + } + + if (hdr->e_ident[EI_MAG3] != ELF_MAG3) { + return false; + } + + if (hdr->e_ident[EI_CLASS] != ELFCLASS64) { + return false; + } + + if (hdr->e_machine != EM_X86_64) { + return false; + } + + if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) { + return false; + } + + if (hdr->e_ident[EI_VERSION] != EV_CURRENT) { + return false; + } + + return true; +} + +static int map_image(struct elf_image *image) +{ + elf_phdr_t phdr; + size_t r = 0; + + size_t data_offset = 0; + + for (size_t i = 0; i < image->e_hdr.e_phnum; i++) { + off_t phdr_offset + = image->e_hdr.e_phoff + (i * image->e_hdr.e_phentsize); + lseek(image->e_fd, phdr_offset, SEEK_SET); + int r = read(image->e_fd, &phdr, sizeof phdr); + + if (r < 0) { + return -r; + } + + if (r != sizeof phdr) { + return ENOEXEC; + } + + if (phdr.p_type != PT_LOAD) { + continue; + } + + int prot = 0; + size_t offset = phdr.p_offset & ~PAGE_MASK; + + phdr.p_flags &PF_R && (prot |= PROT_READ); + phdr.p_flags &PF_W && (prot |= PROT_WRITE); + phdr.p_flags &PF_X && (prot |= PROT_EXEC); + + virt_addr_t vaddr = phdr.p_vaddr; + virt_addr_t vlimit = phdr.p_vaddr + phdr.p_memsz; + if (vaddr & PAGE_MASK) { + vaddr &= ~PAGE_MASK; + } + + if (vlimit & PAGE_MASK) { + vlimit &= ~PAGE_MASK; + vlimit += PAGE_SIZE; + } + + if (image->e_hdr.e_type == ET_DYN) { + vaddr += image->e_base; + vlimit += image->e_base; + } + + int fd = image->e_fd; + int flags = MAP_SHARED | MAP_EXECUTABLE | MAP_FIXED; + + if (phdr.p_flags & PF_W) { + fd = -1; + flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; + offset = 0; + } + + void *p + = mmap((void *)vaddr, + vlimit - vaddr, + prot, + flags, + fd, + offset); + if (p == MAP_FAILED) { + return EIO; + } + + kern_tracef( + "mapped PHDR %u [%zx-%zx] at %p", + i, + phdr.p_vaddr, + phdr.p_vaddr + phdr.p_memsz, + p); + + if (phdr.p_flags & PF_W) { + lseek(image->e_fd, phdr.p_offset, SEEK_SET); + void *dst = (void *)image->e_base + phdr.p_vaddr; + r = read(image->e_fd, dst, phdr.p_filesz); + if (r < 0) { + return -r; + } + } + } + + return SUCCESS; +} + +static int parse_phdr(struct elf_image *image) +{ + elf_phdr_t phdr; + size_t r = 0; + image->e_length = 0; + image->e_data_length = 0; + off_t vaddr, vlimit; + + for (size_t i = 0; i < image->e_hdr.e_phnum; i++) { + off_t phdr_offset + = image->e_hdr.e_phoff + (i * image->e_hdr.e_phentsize); + lseek(image->e_fd, phdr_offset, SEEK_SET); + int r = read(image->e_fd, &phdr, sizeof phdr); + + if (r < 0) { + return -r; + } + + if (r != sizeof phdr) { + return ENOEXEC; + } + + vaddr = phdr.p_vaddr; + vlimit = phdr.p_vaddr + phdr.p_memsz; + if (vaddr & (PAGE_SIZE - 1)) { + vaddr &= ~(PAGE_SIZE - 1); + } + + if (vlimit & (PAGE_SIZE - 1)) { + vlimit &= ~(PAGE_SIZE - 1); + vlimit += PAGE_SIZE; + } + + switch (phdr.p_type) { + case PT_DYNAMIC: + image->e_dynamic = phdr; + break; + case PT_LOAD: + image->e_length = MAX(image->e_length, vlimit); + break; +#if 0 + case PT_INTERP: { + size_t r = 0; + vm_object_read( + image->e_image, + image->e_interp, + phdr.p_offset, + MIN(sizeof image->e_interp - 1, phdr.p_filesz), + &r); + image->e_interp[r] = 0; + break; + } +#endif + default: + break; + } + + if (phdr.p_flags & PF_W) { + image->e_data_length + = MAX(image->e_data_length, vlimit - vaddr); + } + } + + return SUCCESS; +} + +#if 1 +static elf_sym_t *get_dynsym(struct elf_image *image, size_t index) +{ + elf_sym_t *sym = (elf_sym_t *)(image->e_base + image->e_dynsym + + (index * image->e_dynsym_entsize)); + + if (!sym->st_value) { + return NULL; + } + + return sym; +} + +static void resolve_symbol(unsigned int slot) +{ + kern_tracef("request for symbol %u", slot); +} + +static int do_rela(struct elf_image *image, elf_rela_t *rela, bool lazy) +{ + kern_tracef( + "do_rela(%p, %d, %d, %d)", + image, + rela->r_info, + rela->r_addend, + rela->r_offset); + int type = ELF64_R_TYPE(rela->r_info); + elf_sym_t *sym = NULL; + + switch (type) { + case R_X86_64_JUMP_SLOT: + *(uint64_t *)(image->e_base + rela->r_offset) += image->e_base; + kern_tracef( + "JUMP_SLOT: offset=%zx, symbol=%zu, addend=%zx", + rela->r_offset, + ELF64_R_SYM(rela->r_info), + rela->r_addend); + break; + case R_X86_64_RELATIVE: + *(uint64_t *)(image->e_base + rela->r_offset) + = image->e_base + rela->r_addend; + kern_tracef( + "RELATIVE: offset=%zx, addend=%zx", + rela->r_offset, + rela->r_addend); + break; + default: + kern_log("Unknown relocation type"); + return ENOEXEC; + } + + return SUCCESS; +} + +static int relocate_pltrel( + struct elf_image *image, + off_t offset, + size_t size, + size_t entsize) +{ + size_t entries = size / entsize; + elf_rela_t *rela = (elf_rela_t *)(image->e_base + offset); + int status = SUCCESS; + + for (size_t i = 0; i < entries; i++) { + status = do_rela(image, rela, true); + + if (status != SUCCESS) { + break; + } + + rela = (elf_rela_t *)((char *)rela + entsize); + } + + return status; +} + +static int relocate_rela( + struct elf_image *image, + off_t offset, + size_t size, + size_t entsize) +{ + size_t entries = size / entsize; + elf_rela_t *rela = (elf_rela_t *)(image->e_base + offset); + int status = SUCCESS; + + for (size_t i = 0; i < entries; i++) { + status = do_rela(image, rela, false); + + if (status != SUCCESS) { + break; + } + + rela = (elf_rela_t *)((char *)rela + entsize); + } + + return status; +} + +static int relocate_rel( + struct elf_image *image, + off_t offset, + size_t size, + size_t entsize) +{ + return ENOEXEC; +} + +static int do_rel( + struct elf_image *image, + off_t offset, + size_t size, + size_t entsize) + +{ + kern_tracef("do_rel (unsupported)"); + return ENOEXEC; +} +#endif + +static int load_dependency(struct elf_image *image, const char *name) +{ + kern_tracef("required library: %s", name); + return ENOEXEC; +} + +static int parse_dynamic(struct elf_image *image) +{ + if (image->e_dynamic.p_type != PT_DYNAMIC) { + return SUCCESS; + } + + image->e_dyn = (elf_dyn_t *)(image->e_base + image->e_dynamic.p_vaddr); + + int status = SUCCESS; + + size_t nr_dyn = image->e_dynamic.p_filesz / sizeof *image->e_dyn; + for (size_t i = 0; i < nr_dyn; i++) { + if (image->e_dyn[i].d_tag == DT_NULL) { + break; + } + + switch (image->e_dyn[i].d_tag) { + case DT_NEEDED: + image->e_nr_links++; + break; + case DT_STRTAB: + image->e_strtab = image->e_dyn[i].d_un.d_ptr; + break; + case DT_SYMTAB: + image->e_dynsym = image->e_dyn[i].d_un.d_ptr; + break; + case DT_SYMENT: + image->e_dynsym_entsize = image->e_dyn[i].d_un.d_val; + break; + case DT_PLTGOT: + image->e_got_plt = image->e_dyn[i].d_un.d_val; + break; + case DT_HASH: + image->e_hash_type = ELF_HASH_STANDARD; + image->e_hash_table = image->e_dyn[i].d_un.d_ptr; + break; + case DT_GNU_HASH: + image->e_hash_type = ELF_HASH_GNU; + image->e_hash_table = image->e_dyn[i].d_un.d_ptr; + break; + case DT_REL: + image->e_rel_offset[ELF_RT_REL] + = image->e_dyn[i].d_un.d_ptr; + break; + case DT_RELSZ: + image->e_rel_size[ELF_RT_REL] + = image->e_dyn[i].d_un.d_val; + break; + case DT_RELENT: + image->e_rel_entsize[ELF_RT_REL] + = image->e_dyn[i].d_un.d_val; + break; + case DT_RELA: + image->e_rel_offset[ELF_RT_RELA] + = image->e_dyn[i].d_un.d_ptr; + break; + case DT_RELASZ: + image->e_rel_size[ELF_RT_RELA] + = image->e_dyn[i].d_un.d_val; + break; + case DT_RELAENT: + image->e_rel_entsize[ELF_RT_RELA] + = image->e_dyn[i].d_un.d_val; + break; + case DT_PLTREL: + image->e_pltrel_type = image->e_dyn[i].d_un.d_val; + switch (image->e_pltrel_type) { + case DT_REL: + image->e_rel_entsize[ELF_RT_PLTREL] = 0; + break; + case DT_RELA: + image->e_rel_entsize[ELF_RT_PLTREL] + = sizeof(elf_rela_t); + break; + default: + break; + } + break; + case DT_JMPREL: + image->e_rel_offset[ELF_RT_PLTREL] + = image->e_dyn[i].d_un.d_ptr; + break; + case DT_PLTRELSZ: + image->e_rel_size[ELF_RT_PLTREL] + = image->e_dyn[i].d_un.d_val; + break; + default: + break; + } + + image->e_dyn_count++; + } + + return SUCCESS; +} + +static int reserve_exec_region(struct elf_image *image) +{ + void *base + = mmap(NULL, + image->e_length, + PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE, + -1, + 0); + if (base == MAP_FAILED) { + return ENOMEM; + } + + image->e_base = (virt_addr_t)base; + return KERN_OK; +} + +static int create_image_with_name(const char *name, struct elf_image **out) +{ + struct elf_image *elf = malloc(sizeof *elf); + if (!elf) { + return ENOMEM; + } + + memset(elf, 0x0, sizeof *elf); + + snprintf(elf->e_leaf.l_name, sizeof elf->e_leaf.l_name, "%s", name); + + kern_config_get( + KERN_CFG_PAGE_SIZE, + &elf->e_page_size, + sizeof elf->e_page_size); + + *out = elf; + return SUCCESS; +} + +int elf_image_open(const char *path, struct elf_image **out) +{ + struct elf_image *elf = malloc(sizeof *elf); + if (!elf) { + return ENOMEM; + } + + memset(elf, 0x0, sizeof *elf); + + kern_config_get( + KERN_CFG_PAGE_SIZE, + &elf->e_page_size, + sizeof elf->e_page_size); + + int fd = open(path, O_RDONLY); + if (fd < 0) { + elf_image_close(elf); + return -fd; + } + + elf->e_status = ELF_IMAGE_OPEN; + elf->e_fd = fd; + + *out = elf; + return SUCCESS; +} + +int elf_image_parse(struct elf_image *img) +{ + if (img->e_status != ELF_IMAGE_OPEN) { + return EINVAL; + } + + int e = read(img->e_fd, &img->e_hdr, sizeof img->e_hdr); + if (e < 0) { + return -e; + } + + if (e != sizeof img->e_hdr) { + return ENOEXEC; + } + + if (!elf_validate_ehdr(&img->e_hdr)) { + return ENOEXEC; + } + + e = parse_phdr(img); + if (e != SUCCESS) { + return e; + } + + img->e_status = ELF_IMAGE_PARSED; + return SUCCESS; +} + +int elf_image_load(struct elf_image *img) +{ + if (img->e_status != ELF_IMAGE_PARSED) { + return EINVAL; + } + + int e = reserve_exec_region(img); + if (e != SUCCESS) { + return e; + } + + e = map_image(img); + if (e != SUCCESS) { + return e; + } + + e = parse_dynamic(img); + if (e != SUCCESS) { + return e; + } + + img->e_status = ELF_IMAGE_LOADED; + return SUCCESS; +} + +int elf_image_link(struct elf_image *img) +{ + if (img->e_status != ELF_IMAGE_LOADED) { + return EINVAL; + } + + int status = SUCCESS; + + if (img->e_rel_offset[ELF_RT_REL]) { + status = relocate_rel( + img, + img->e_rel_offset[ELF_RT_REL], + img->e_rel_size[ELF_RT_REL], + img->e_rel_entsize[ELF_RT_REL]); + + if (status != SUCCESS) { + return status; + } + } + + if (img->e_rel_offset[ELF_RT_RELA]) { + status = relocate_rela( + img, + img->e_rel_offset[ELF_RT_RELA], + img->e_rel_size[ELF_RT_RELA], + img->e_rel_entsize[ELF_RT_RELA]); + + if (status != SUCCESS) { + return status; + } + } + +#if 1 + if (img->e_rel_offset[ELF_RT_PLTREL]) { + status = relocate_pltrel( + img, + img->e_rel_offset[ELF_RT_PLTREL], + img->e_rel_size[ELF_RT_PLTREL], + img->e_rel_entsize[ELF_RT_PLTREL]); + + if (status != SUCCESS) { + return status; + } + } +#endif + *(uintptr_t *)(img->e_base + img->e_got_plt + 16) + = (uintptr_t)_dl_runtime_resolve; + *(uintptr_t *)(img->e_base + img->e_got_plt + 8) = (uintptr_t)img; + + img->e_entry = (virt_addr_t)img->e_base + img->e_hdr.e_entry; + img->e_status = ELF_IMAGE_LINKED; + return SUCCESS; +} + +extern int elf_image_collect_dependencies( + struct elf_image *img, + struct image_list *dest) +{ + if (!img->e_nr_links || img->e_links) { + return SUCCESS; + } + + int nr_added = 0; + img->e_links = calloc(img->e_nr_links, sizeof(struct elf_image *)); + + for (size_t i = 0; i < img->e_dyn_count; i++) { + if (img->e_dyn[i].d_tag != DT_NEEDED) { + continue; + } + + const char *name = (const char *)img->e_base + img->e_strtab + + img->e_dyn[i].d_un.d_val; + + if (image_list_get(dest, name)) { + continue; + } + + struct elf_image *dep = NULL; + int status = create_image_with_name(name, &dep); + if (status != SUCCESS) { + return -status; + } + + image_list_put(dest, &dep->e_leaf); + img->e_links[nr_added] = dep; + nr_added++; + } + + return nr_added; +} + +void elf_image_close(struct elf_image *image) +{ + if (image->e_fd) { + close(image->e_fd); + } + + free(image); +} + +static uint32_t std_hash(const char *name) +{ + uint32_t h = 0, g; + for (; *name; name++) { + h = (h << 4) + *name; + if ((g = h & 0xf0000000)) { + h ^= g >> 24; + } + h &= ~g; + } + return h; +} + +static uint32_t gnu_hash(const char *name) +{ + uint32_t h = 5381; + + for (; *name; name++) { + h = (h << 5) + h + *name; + } + + return h; +} + +static virt_addr_t find_symbol_stdhash( + struct elf_image *img, + const char *name, + uint32_t hash) +{ + const uint32_t *hashtab + = (void *)((virt_addr_t)img->e_base + img->e_hash_table); + + const char *strtab = (void *)((virt_addr_t)img->e_base + img->e_strtab); + + const elf_sym_t *symtab + = (void *)((virt_addr_t)img->e_base + img->e_dynsym); + + const uint32_t nbucket = hashtab[0]; + const uint32_t nchain = hashtab[1]; + const uint32_t *bucket = &hashtab[2]; + const uint32_t *chain = &bucket[nbucket]; + + for (uint32_t i = bucket[hash % nbucket]; i; i = chain[i]) { + if (strcmp(name, strtab + symtab[i].st_name) == 0) { + return img->e_base + symtab[i].st_value; + } + } + + return 0; +} + +static virt_addr_t find_symbol_gnuhash( + struct elf_image *img, + const char *name, + uint32_t hash) +{ + return 0; +} + +static virt_addr_t find_symbol_slow(struct elf_image *img, const char *name) +{ + return 0; +} + +static virt_addr_t find_symbol( + struct elf_image *img, + const char *name, + uint32_t std_hash, + uint32_t gnu_hash) +{ + switch (img->e_hash_type) { + case ELF_HASH_STANDARD: + return find_symbol_stdhash(img, name, std_hash); + case ELF_HASH_GNU: + return find_symbol_gnuhash(img, name, gnu_hash); + default: + return find_symbol_slow(img, name); + } +} + +virt_addr_t elf_image_find_symbol(struct elf_image *img, const char *name) +{ + uint32_t std_hash_val = std_hash(name); + uint32_t gnu_hash_val = gnu_hash(name); + return find_symbol(img, name, std_hash_val, gnu_hash_val); +} + +virt_addr_t elf_image_find_linked_symbol( + struct elf_image *img, + const char *name) +{ + uint32_t std_hash_val = std_hash(name); + uint32_t gnu_hash_val = gnu_hash(name); + + virt_addr_t sym = 0; + for (size_t i = 0; i < img->e_nr_links; i++) { + sym = find_symbol( + img->e_links[i], + name, + std_hash_val, + gnu_hash_val); + if (sym) { + break; + } + } + + return sym; +} diff --git a/sys/ld/elf.h b/sys/ld/elf.h new file mode 100644 index 0000000..13045a5 --- /dev/null +++ b/sys/ld/elf.h @@ -0,0 +1,365 @@ +#ifndef LD_ELF_H_ +#define LD_ELF_H_ + +#include "image-list.h" + +#include +#include + +enum elf_image_status { + ELF_IMAGE_NONE = 0, + ELF_IMAGE_OPEN, + ELF_IMAGE_PARSED, + ELF_IMAGE_LOADED, + ELF_IMAGE_LINKED, +}; + +enum elf_hash_type { + ELF_HASH_NONE = 0, + ELF_HASH_STANDARD, + ELF_HASH_GNU, +}; + +enum elf_relocation_type { + ELF_RT_NONE = 0, + ELF_RT_REL, + ELF_RT_RELA, + ELF_RT_PLTREL, + ELF_RT_COUNT, +}; + +#define ELF_LOAD_ERR -1 +#define ELF_LOADED_EXEC 0 +#define ELF_LOADED_INTERP 1 + +#define ELF_MAG0 0x7f +#define ELF_MAG1 'E' +#define ELF_MAG2 'L' +#define ELF_MAG3 'F' +#define ELF_NIDENT 16 + +#define SHT_NONE 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_DYNAMIC 6 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_DYNSYM 11 + +/** Little endian. */ +#define ELFDATA2LSB (1) + +/** 64-bit. */ +#define ELFCLASS64 (2) + +/** x86_64 machine type. */ +#define EM_X86_64 (62) + +/** ELF current version. */ +#define EV_CURRENT (1) + +/** Dynamic section tags. */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_JMPREL 23 +#define DT_GNU_HASH 0x6ffffef5 +#define DT_AUXILIARY 0x7ffffffd + +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_GOT32X 43 + +#define R_X86_64_64 1 +#define R_X86_64_PC32 2 +#define R_X86_64_GOT32 3 +#define R_X86_64_PLT32 4 +#define R_X86_64_COPY 5 +#define R_X86_64_GLOB_DAT 6 +#define R_X86_64_JUMP_SLOT 7 +#define R_X86_64_RELATIVE 8 +#define R_X86_64_GOTPCREL 9 +#define R_X86_64_32 10 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +/* Section flags */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 + +#define SHN_UNDEF 0 + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((elf_word_t)(i)) +#define ELF64_ST_BIND(i) ((i) >> 4) +#define ELF64_ST_TYPE(i) ((i) & 0xf) + +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_NUM 3 + +typedef uint64_t elf_addr_t; +typedef uint64_t elf_off_t; +typedef uint16_t elf_half_t; +typedef uint32_t elf_word_t; +typedef int32_t elf_sword_t; +typedef uint64_t elf_xword_t; +typedef int64_t elf_sxword_t; + +/** + * ELF file header. + */ +typedef struct { + uint8_t e_ident[ELF_NIDENT]; + elf_half_t e_type; + elf_half_t e_machine; + elf_word_t e_version; + elf_addr_t e_entry; + elf_off_t e_phoff; + elf_off_t e_shoff; + elf_word_t e_flags; + elf_half_t e_ehsize; + elf_half_t e_phentsize; + elf_half_t e_phnum; + elf_half_t e_shentsize; + elf_half_t e_shnum; + elf_half_t e_shstrndx; +} elf_ehdr_t; + +/** + * ELF section header. + */ +typedef struct { + elf_word_t sh_name; + elf_word_t sh_type; + elf_xword_t sh_flags; + elf_addr_t sh_addr; + elf_off_t sh_offset; + elf_xword_t sh_size; + elf_word_t sh_link; + elf_word_t sh_info; + elf_xword_t sh_addralign; + elf_xword_t sh_entsize; +} elf_shdr_t; + +/** + * ELF symbol. + */ +typedef struct { + elf_word_t st_name; + unsigned char st_info; + unsigned char st_other; + elf_half_t st_shndx; + elf_addr_t st_value; + elf_xword_t st_size; +} elf_sym_t; + +/** + * ELF program header. + */ +typedef struct { + elf_word_t p_type; + elf_word_t p_flags; + elf_off_t p_offset; + elf_addr_t p_vaddr; + elf_addr_t p_paddr; + elf_xword_t p_filesz; + elf_xword_t p_memsz; + elf_xword_t p_align; +} elf_phdr_t; + +/** + * Extended ELF relocation information. + */ +typedef struct { + elf_addr_t r_offset; + elf_xword_t r_info; + elf_sxword_t r_addend; +} elf_rela_t; + +/** + * Dynamic section entries + */ +typedef struct { + elf_sxword_t d_tag; + union { + elf_xword_t d_val; + elf_addr_t d_ptr; + } d_un; +} elf_dyn_t; + +/** + * Section header types. + */ +enum elf_stype { + ST_NONE = 0, + ST_PROGBITS = 1, + ST_SYMTAB = 2, + ST_STRTAB = 3, + ST_NOBITS = 8, + ST_REL = 9 +}; + +/** + * Program header types. + */ +enum elf_ptype { + PT_NULL = 0, + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_INTERP = 3, + PT_NOTE = 4, + PT_SHLIB = 5, + PT_PHDR = 6 +}; + +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 + +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7FFFFFFF + +/** + * ELF identification byte locations. + */ +enum elf_ident { + EI_MAG0 = 0, + EI_MAG1 = 1, + EI_MAG2 = 2, + EI_MAG3 = 3, + EI_CLASS = 4, + EI_DATA = 5, + EI_VERSION = 6, + EI_OSABI = 7, + EI_ABIVERSION = 8, + EI_PAD = 9 +}; + +enum elf_type { + ET_NONE = 0, + ET_REL = 1, + ET_EXEC = 2, + ET_DYN = 3, +}; + +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#define AT_NOTELF 10 +#define AT_UID 11 +#define AT_EUID 12 +#define AT_GID 13 +#define AT_EGID 14 +#define AT_CLKTCK 17 +#define AT_PLATFORM 15 +#define AT_HWCAP 16 +#define AT_FPUCW 18 +#define AT_DCACHEBSIZE 19 +#define AT_ICACHEBSIZE 20 +#define AT_UCACHEBSIZE 21 +#define AT_IGNOREPPC 22 +#define AT_SECURE 23 +#define AT_BASE_PLATFORM 24 +#define AT_RANDOM 25 +#define AT_HWCAP2 26 +#define AT_EXECFN 31 +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 +#define AT_ENTRY_COUNT 38 + +struct bootdata; +struct bootfs_file; + +struct elf_image { + enum elf_image_status e_status; + struct image_list_leaf e_leaf; + + int e_fd; + size_t e_page_size; + elf_ehdr_t e_hdr; + virt_addr_t e_base; + size_t e_length, e_data_length; + virt_addr_t e_entry; + + virt_addr_t e_strtab; + + enum elf_hash_type e_hash_type; + virt_addr_t e_hash_table; + + virt_addr_t e_got_plt; + virt_addr_t e_dynsym; + size_t e_dynsym_entsize; + + elf_phdr_t e_dynamic; + elf_dyn_t *e_dyn; + size_t e_dyn_count; + + int e_pltrel_type; + off_t e_rel_offset[ELF_RT_COUNT]; + size_t e_rel_size[ELF_RT_COUNT]; + size_t e_rel_entsize[ELF_RT_COUNT]; + + struct elf_image **e_links; + size_t e_nr_links; +}; + +extern const char *elf_image_status_to_string(enum elf_image_status status); + +extern int elf_image_open(const char *path, struct elf_image **out); +extern int elf_image_parse(struct elf_image *img); +extern int elf_image_load(struct elf_image *img); +extern int elf_image_collect_dependencies( + struct elf_image *img, + struct image_list *dest); +extern int elf_image_link(struct elf_image *img); +extern void elf_image_close(struct elf_image *img); + +extern virt_addr_t elf_image_find_symbol( + struct elf_image *img, + const char *name); +extern virt_addr_t elf_image_find_linked_symbol( + struct elf_image *img, + const char *name); + +#endif diff --git a/sys/ld/hash.c b/sys/ld/hash.c new file mode 100644 index 0000000..1770020 --- /dev/null +++ b/sys/ld/hash.c @@ -0,0 +1,16 @@ +#include + +#define BASIS 0xcbf29ce484222325 +#define PRIME 0x100000001b3 + +uint64_t hash_string(const char *s) +{ + uint64_t result = BASIS; + + for (unsigned int i = 0; s[i]; i++) { + result ^= s[i]; + result *= PRIME; + } + + return result; +} diff --git a/sys/ld/hash.h b/sys/ld/hash.h new file mode 100644 index 0000000..023a34e --- /dev/null +++ b/sys/ld/hash.h @@ -0,0 +1,8 @@ +#ifndef HASH_H_ +#define HASH_H_ + +#include + +extern uint64_t hash_string(const char *s); + +#endif diff --git a/sys/ld/image-list.c b/sys/ld/image-list.c new file mode 100644 index 0000000..8df67a9 --- /dev/null +++ b/sys/ld/image-list.c @@ -0,0 +1,211 @@ +#include "image-list.h" + +#include "hash.h" + +#include +#include +#include +#include + +static BTREE_DEFINE_SIMPLE_GET( + struct image_list_entry, + uint64_t, + e_node, + e_hash, + get_entry); +static BTREE_DEFINE_SIMPLE_INSERT( + struct image_list_entry, + e_node, + e_hash, + put_entry); + +void image_list_init(struct image_list *list) +{ + memset(list, 0x0, sizeof *list); +} + +void image_list_cleanup(struct image_list *list) +{ +} + +extern struct image_list_bucket *convert_to_bucket( + struct btree *tree, + struct image_list_leaf *leaf) +{ + btree_delete(tree, &leaf->l_base.e_node); + + struct image_list_bucket *bucket = malloc(sizeof *bucket); + if (!bucket) { + return NULL; + } + + bucket->b_base.e_hash = leaf->l_base.e_hash; + bucket->b_base.e_type = IMAGE_LIST_ENTRY_BUCKET; + put_entry(tree, &bucket->b_base); + + queue_push_back(&bucket->b_items, &leaf->l_base.e_entry); + + return bucket; +} + +extern void image_list_put( + struct image_list *list, + struct image_list_leaf *item) +{ + uint64_t hash = hash_string(item->l_name); + item->l_base.e_type = IMAGE_LIST_ENTRY_LEAF; + item->l_base.e_hash = hash; + + struct image_list_entry *entry = get_entry(&list->l_items, hash); + if (!entry) { + put_entry(&list->l_items, &item->l_base); + return; + } + + if (entry->e_type == IMAGE_LIST_ENTRY_BUCKET) { + struct image_list_bucket *bucket + = (struct image_list_bucket *)entry; + queue_push_back(&bucket->b_items, &item->l_base.e_entry); + return; + } + + struct image_list_leaf *leaf = (struct image_list_leaf *)entry; + struct image_list_bucket *bucket + = convert_to_bucket(&list->l_items, leaf); + if (!bucket) { + return; + } + + queue_push_back(&bucket->b_items, &item->l_base.e_entry); +} + +extern struct image_list_leaf *image_list_get( + struct image_list *list, + const char *name) +{ + uint64_t hash = hash_string(name); + struct image_list_entry *entry = get_entry(&list->l_items, hash); + if (!entry) { + return NULL; + } + + switch (entry->e_type) { + case IMAGE_LIST_ENTRY_LEAF: { + struct image_list_leaf *leaf = (struct image_list_leaf *)entry; + if (!strcmp(leaf->l_name, name)) { + return leaf; + } + break; + } + + case IMAGE_LIST_ENTRY_BUCKET: { + struct image_list_bucket *bucket + = (struct image_list_bucket *)entry; + struct queue_entry *cur = queue_first(&bucket->b_items); + while (cur) { + struct image_list_leaf *leaf = QUEUE_CONTAINER( + struct image_list_leaf, + l_base.e_entry, + cur); + if (!strcmp(leaf->l_name, name)) { + return leaf; + } + + cur = queue_next(cur); + break; + } + } + + default: + break; + } + + return NULL; +} + +void image_list_iterator_begin( + struct image_list_iterator *it, + struct image_list *list) +{ + memset(it, 0x0, sizeof *it); + struct btree_node *node = btree_first(&list->l_items); + if (!node) { + return; + } + + while (1) { + it->it_cur = QUEUE_CONTAINER( + struct image_list_entry, + e_node, + node); + if (it->it_cur->e_type == IMAGE_LIST_ENTRY_LEAF) { + it->it_leaf = (struct image_list_leaf *)it->it_cur; + return; + } + + struct image_list_bucket *bucket + = (struct image_list_bucket *)it->it_cur; + struct queue_entry *entry = queue_first(&bucket->b_items); + if (!entry) { + node = btree_next(node); + continue; + } + + it->it_leaf = QUEUE_CONTAINER( + struct image_list_leaf, + l_base.e_entry, + entry); + break; + } +} + +void image_list_iterator_move_next(struct image_list_iterator *it) +{ + if (!it->it_cur || !it->it_leaf) { + return; + } + + while (1) { + if (it->it_cur->e_type == IMAGE_LIST_ENTRY_LEAF) { + /* current entry is a leaf */ + struct queue_entry *next + = queue_next(&it->it_leaf->l_base.e_entry); + if (next) { + it->it_leaf = QUEUE_CONTAINER( + struct image_list_leaf, + l_base.e_entry, + next); + } + } + + struct btree_node *node = btree_next(&it->it_cur->e_node); + if (!node) { + it->it_cur = NULL; + it->it_leaf = NULL; + return; + } + + it->it_cur = BTREE_CONTAINER( + struct image_list_entry, + e_node, + node); + if (it->it_cur->e_type == IMAGE_LIST_ENTRY_LEAF) { + /* next entry is a leaf */ + it->it_leaf = (struct image_list_leaf *)it->it_cur; + return; + } + + struct image_list_bucket *bucket + = (struct image_list_bucket *)it->it_cur; + struct queue_entry *entry = queue_first(&bucket->b_items); + if (!entry) { + continue; + } + + it->it_leaf = QUEUE_CONTAINER( + struct image_list_leaf, + l_base.e_entry, + entry); + break; + } +} diff --git a/sys/ld/image-list.h b/sys/ld/image-list.h new file mode 100644 index 0000000..d747818 --- /dev/null +++ b/sys/ld/image-list.h @@ -0,0 +1,59 @@ +#ifndef IMAGE_LIST_H_ +#define IMAGE_LIST_H_ + +#include "btree.h" +#include "queue.h" + +#include + +#define IMAGE_NAME_MAX 256 + +enum image_list_entry_type { + IMAGE_LIST_ENTRY_NONE = 0, + IMAGE_LIST_ENTRY_LEAF, + IMAGE_LIST_ENTRY_BUCKET, +}; + +struct image_list_entry { + enum image_list_entry_type e_type; + uint64_t e_hash; + union { + struct btree_node e_node; + struct queue_entry e_entry; + }; +}; + +struct image_list_bucket { + struct image_list_entry b_base; + struct queue b_items; +}; + +struct image_list_leaf { + struct image_list_entry l_base; + char l_name[IMAGE_NAME_MAX]; +}; + +struct image_list { + struct btree l_items; +}; + +struct image_list_iterator { + struct image_list_entry *it_cur; + struct image_list_leaf *it_leaf; +}; + +extern void image_list_init(struct image_list *list); +extern void image_list_cleanup(struct image_list *list); + +extern void image_list_put( + struct image_list *list, + struct image_list_leaf *item); +extern struct image_list_leaf *image_list_get( + struct image_list *list, + const char *name); + +extern void image_list_iterator_begin( + struct image_list_iterator *it, + struct image_list *list); +extern void image_list_iterator_move_next(struct image_list_iterator *it); +#endif diff --git a/sys/ld/main.c b/sys/ld/main.c index b84ad0a..a98185f 100644 --- a/sys/ld/main.c +++ b/sys/ld/main.c @@ -1,7 +1,10 @@ #define MSG_IMPLEMENTATION #define MSG_NO_MALLOC +#include "elf.h" + #include +#include #include #include #include @@ -10,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -17,84 +21,204 @@ #include #include +static const char *search_paths[] = { + "/usr/lib", +}; +static const size_t nr_search_paths + = sizeof search_paths / sizeof search_paths[0]; + +static void report_error(const char *name, int err, const char *msg, ...) +{ + char buf[1024]; + va_list arg; + va_start(arg, msg); + vsnprintf(buf, sizeof buf, msg, arg); + va_end(arg); + + kern_tracef("%s: %s: %s", name, buf, strerror(err)); +} + +static const char *get_image_name(const char *path) +{ + const char *last_slash = NULL; + for (size_t i = 0; path[i]; i++) { + if (path[i] == '/') { + last_slash = path + i; + } + } + + return last_slash ? last_slash + 1 : path; +} + +static int find_image(struct elf_image *img) +{ + const char *name = img->e_leaf.l_name; + char path[4096]; + + for (size_t i = 0; i < nr_search_paths; i++) { + snprintf(path, sizeof path, "%s/%s", search_paths[i], name); + int fd = open(path, O_RDONLY); + if (fd < 0) { + continue; + } + + kern_tracef("found %s -> %s", name, path); + img->e_fd = fd; + img->e_status = ELF_IMAGE_OPEN; + return SUCCESS; + } + + return ENOENT; +} + +static int load_images(struct image_list *list) +{ + int status = SUCCESS; + struct image_list_iterator it; + image_list_iterator_begin(&it, list); + + while (it.it_leaf) { + struct elf_image *image + = QUEUE_CONTAINER(struct elf_image, e_leaf, it.it_leaf); + kern_tracef( + "image: %s [%s]", + it.it_leaf->l_name, + elf_image_status_to_string(image->e_status)); + + int new_dependencies = 0; + switch (image->e_status) { + case ELF_IMAGE_NONE: + /* Find the image using its name */ + status = find_image(image); + if (status != SUCCESS) { + return status; + } + case ELF_IMAGE_OPEN: + /* parse the image */ + status = elf_image_parse(image); + if (status != SUCCESS) { + return status; + } + case ELF_IMAGE_PARSED: + /* load the image */ + status = elf_image_load(image); + if (status != SUCCESS) { + return status; + } + case ELF_IMAGE_LOADED: + /* collect dependencies */ + new_dependencies + = elf_image_collect_dependencies(image, list); + default: + break; + } + + if (new_dependencies < 0) { + return -new_dependencies; + } + + if (new_dependencies > 0) { + image_list_iterator_begin(&it, list); + } else { + image_list_iterator_move_next(&it); + } + } + + return SUCCESS; +} + +static int link_images(struct image_list *list) +{ + int status = SUCCESS; + struct image_list_iterator it; + image_list_iterator_begin(&it, list); + + kern_trace("linking all images"); + while (it.it_leaf) { + struct elf_image *image + = QUEUE_CONTAINER(struct elf_image, e_leaf, it.it_leaf); + kern_tracef( + "image: %s [%s]", + it.it_leaf->l_name, + elf_image_status_to_string(image->e_status)); + + status = elf_image_link(image); + + image_list_iterator_move_next(&it); + } + return SUCCESS; +} + int main(const struct rosetta_bootstrap *bs) { - kern_logf("ld"); + kern_tracef("ld"); for (size_t i = 0; i < bs->bs_argc; i++) { - kern_logf("argv[%zu]: %s", i, bs->bs_argv[i]); + kern_tracef("argv[%zu]: %s", i, bs->bs_argv[i]); } sys_remote_set(SYS_REMOTE_NSD, 0, 0); - const char *path = "/usr/lib/libc.so"; - int flags = 4; + const char *exec_path = bs->bs_argv[1]; + const char *task_name = bs->bs_argv[2]; + const char *image_name = get_image_name(exec_path); - kern_logf("sending msg: open(%s, %d)", path, flags); - int fd = open(path, flags); + struct image_list images; + image_list_init(&images); - if (fd < 0) { - kern_logf( - "open(%s, %d) = %s (%s)", - path, - flags, - strerror_code(fd), - strerror(fd)); + struct elf_image *exec = NULL; + int err = elf_image_open(exec_path, &exec); + if (err != SUCCESS) { + report_error( + task_name, + err, + "error while loading %s", + exec_path); return -1; } - kern_logf( - "open(%s, %d) = %s (%s)", - path, - flags, - strerror_code(SUCCESS), - strerror(SUCCESS)); + snprintf( + exec->e_leaf.l_name, + sizeof exec->e_leaf.l_name, + "%s", + image_name); - unsigned char buf[32] = {0}; - int nr = read(fd, buf, sizeof buf); - - if (nr < 0) { - kern_logf("read call failed (%s)", strerror(nr)); + image_list_put(&images, &exec->e_leaf); + err = load_images(&images); + if (err != SUCCESS) { + report_error( + task_name, + err, + "error while loading %s", + exec_path); return -1; } - kern_logf("data: %x %c %c %c", buf[0], buf[1], buf[2], buf[3]); - - void *p - = mmap(NULL, - 0x1000, - PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, - -1, - 0); - if (p != MAP_FAILED) { - memset(p, 0x0, 0x1000); - kern_logf("mmap'd buffer at %p", p); - } else { - kern_logf("mmap buffer failed"); + err = link_images(&images); + if (err != SUCCESS) { + report_error( + task_name, + err, + "error while loading %s", + exec_path); + return -1; } - void *lib - = mmap(NULL, 0x2000, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); - if (lib != MAP_FAILED) { - kern_logf("mmap'd %s at %p", path, lib); - unsigned char *tmp = lib; - kern_logf( - "data[0]: %x %c %c %c", - tmp[0], - tmp[1], - tmp[2], - tmp[3]); - tmp += 0x1000; - kern_logf( - "data[0x1000]: %02x %02x %02x %02x", - tmp[0], - tmp[1], - tmp[2], - tmp[3]); - } else { - kern_logf("mmap lib failed"); + struct image_list_iterator it; + image_list_iterator_begin(&it, &images); + + while (it.it_leaf) { + struct elf_image *image + = QUEUE_CONTAINER(struct elf_image, e_leaf, it.it_leaf); + kern_tracef( + "image: %s [%s]", + it.it_leaf->l_name, + elf_image_status_to_string(image->e_status)); + + image_list_iterator_move_next(&it); } - kern_logf("ld finished"); - return 0; + kern_tracef("ld finished"); + int (*entry)(int, const char **) + = (int (*)(int, const char **))exec->e_entry; + return entry(bs->bs_argc - 2, bs->bs_argv + 2); } diff --git a/sys/ld/queue.c b/sys/ld/queue.c new file mode 100644 index 0000000..5c6060e --- /dev/null +++ b/sys/ld/queue.c @@ -0,0 +1,138 @@ +#include "queue.h" + +size_t queue_length(struct queue *q) +{ + size_t i = 0; + struct queue_entry *x = q->q_first; + while (x) { + i++; + x = x->qe_next; + } + + return i; +} + +void queue_insert_before( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *before) +{ + struct queue_entry *x = before->qe_prev; + if (x) { + x->qe_next = entry; + } else { + q->q_first = entry; + } + + entry->qe_prev = x; + + before->qe_prev = entry; + entry->qe_next = before; +} + +void queue_insert_after( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *after) +{ + struct queue_entry *x = after->qe_next; + if (x) { + x->qe_prev = entry; + } else { + q->q_last = entry; + } + + entry->qe_prev = x; + + after->qe_next = entry; + entry->qe_prev = after; +} + +void queue_push_front(struct queue *q, struct queue_entry *entry) +{ + if (q->q_first) { + q->q_first->qe_prev = entry; + } + + entry->qe_next = q->q_first; + entry->qe_prev = NULL; + + q->q_first = entry; + + if (!q->q_last) { + q->q_last = entry; + } +} + +void queue_push_back(struct queue *q, struct queue_entry *entry) +{ + if (q->q_last) { + q->q_last->qe_next = entry; + } + + entry->qe_prev = q->q_last; + entry->qe_next = NULL; + + q->q_last = entry; + + if (!q->q_first) { + q->q_first = entry; + } +} + +struct queue_entry *queue_pop_front(struct queue *q) +{ + struct queue_entry *x = q->q_first; + if (x) { + queue_delete(q, x); + } + + return x; +} + +struct queue_entry *queue_pop_back(struct queue *q) +{ + struct queue_entry *x = q->q_last; + if (x) { + queue_delete(q, x); + } + + return x; +} + +void queue_delete(struct queue *q, struct queue_entry *entry) +{ + if (!entry) { + return; + } + + if (entry == q->q_first) { + q->q_first = q->q_first->qe_next; + } + + if (entry == q->q_last) { + q->q_last = q->q_last->qe_prev; + } + + if (entry->qe_next) { + entry->qe_next->qe_prev = entry->qe_prev; + } + + if (entry->qe_prev) { + entry->qe_prev->qe_next = entry->qe_next; + } + + entry->qe_next = entry->qe_prev = NULL; +} + +void queue_delete_all(struct queue *q) +{ + struct queue_entry *x = q->q_first; + while (x) { + struct queue_entry *next = x->qe_next; + x->qe_next = x->qe_prev = NULL; + x = next; + } + + q->q_first = q->q_last = NULL; +} diff --git a/sys/ld/queue.h b/sys/ld/queue.h new file mode 100644 index 0000000..787fb99 --- /dev/null +++ b/sys/ld/queue.h @@ -0,0 +1,100 @@ +#ifndef QUEUE_H_ +#define QUEUE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define QUEUE_CONTAINER(t, m, v) \ + ((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0)) + +#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL}) +#define QUEUE_ENTRY_INIT \ + ((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL}) + +#define queue_foreach(iter_type, iter_name, queue_name, node_member) \ + for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_first(queue_name)); \ + iter_name; \ + iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_next(&((iter_name)->node_member)))) + +#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \ + for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_last(queue_name)); \ + iter_name; \ + iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_prev(&((iter_name)->node_member)))) + +struct queue_entry { + struct queue_entry *qe_next; + struct queue_entry *qe_prev; +}; + +struct queue { + struct queue_entry *q_first; + struct queue_entry *q_last; +}; + +static inline void queue_init(struct queue *q) +{ + memset(q, 0x00, sizeof *q); +} +static inline bool queue_empty(struct queue *q) +{ + return q->q_first == NULL; +} + +static inline struct queue_entry *queue_first(struct queue *q) +{ + return q->q_first; +} +static inline struct queue_entry *queue_last(struct queue *q) +{ + return q->q_last; +} +static inline struct queue_entry *queue_next(struct queue_entry *entry) +{ + return entry->qe_next; +} +static inline struct queue_entry *queue_prev(struct queue_entry *entry) +{ + return entry->qe_prev; +} + +extern size_t queue_length(struct queue *q); + +extern void queue_insert_before( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *before); +extern void queue_insert_after( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *after); + +extern void queue_push_front(struct queue *q, struct queue_entry *entry); +extern void queue_push_back(struct queue *q, struct queue_entry *entry); + +extern struct queue_entry *queue_pop_front(struct queue *q); +extern struct queue_entry *queue_pop_back(struct queue *q); + +extern void queue_delete(struct queue *q, struct queue_entry *entry); +extern void queue_delete_all(struct queue *q); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sys/ld/resolve.c b/sys/ld/resolve.c new file mode 100644 index 0000000..6949108 --- /dev/null +++ b/sys/ld/resolve.c @@ -0,0 +1,22 @@ +#include "elf.h" + +#include +#include +#include + +uintptr_t dl_runtime_resolve(struct elf_image *img, unsigned long sym_id) +{ + elf_sym_t *sym + = (elf_sym_t *)((virt_addr_t)img->e_base + img->e_dynsym); + const char *sym_name = (const char *)img->e_base + img->e_strtab + + sym[sym_id + 1].st_name; + // kern_logf("%s: request for symbol %s", img->e_leaf.l_name, sym_name); + virt_addr_t sym_addr = elf_image_find_linked_symbol(img, sym_name); + virt_addr_t *sym_slot + = (virt_addr_t *)((virt_addr_t)img->e_base + img->e_got_plt + + ((sym_id + 3) * sizeof(virt_addr_t))); + // kern_logf("symbol %s = %zx", sym_name, sym_addr); + // kern_logf("slot %s = %zx", sym_name, sym_slot); + *sym_slot = sym_addr; + return sym_addr; +} diff --git a/sys/ld/resolve.h b/sys/ld/resolve.h new file mode 100644 index 0000000..50541f4 --- /dev/null +++ b/sys/ld/resolve.h @@ -0,0 +1,7 @@ +#ifndef RESOLVE_H_ +#define RESOLVE_H_ + +extern void _dl_runtime_resolve(void); +extern virt_addr_t dl_runtime_resolve(unsigned int slot); + +#endif