ld: implement an actual program loader

This commit is contained in:
2026-03-21 10:54:50 +00:00
parent ed138d581e
commit cc15bb54f3
14 changed files with 3123 additions and 60 deletions

View File

@@ -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

691
sys/ld/btree.c Normal file
View File

@@ -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 <stddef.h>
#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;
}

475
sys/ld/btree.h Normal file
View File

@@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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

800
sys/ld/elf.c Normal file
View File

@@ -0,0 +1,800 @@
#include "elf.h"
#include "resolve.h"
#include <errno.h>
#include <fcntl.h>
#include <mango/config.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/vm.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#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;
}

365
sys/ld/elf.h Normal file
View File

@@ -0,0 +1,365 @@
#ifndef LD_ELF_H_
#define LD_ELF_H_
#include "image-list.h"
#include <mango/types.h>
#include <stdint.h>
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

16
sys/ld/hash.c Normal file
View File

@@ -0,0 +1,16 @@
#include <stdint.h>
#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;
}

8
sys/ld/hash.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef HASH_H_
#define HASH_H_
#include <stdint.h>
extern uint64_t hash_string(const char *s);
#endif

211
sys/ld/image-list.c Normal file
View File

@@ -0,0 +1,211 @@
#include "image-list.h"
#include "hash.h"
#include <mango/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}
}

59
sys/ld/image-list.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef IMAGE_LIST_H_
#define IMAGE_LIST_H_
#include "btree.h"
#include "queue.h"
#include <stdint.h>
#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

View File

@@ -1,7 +1,10 @@
#define MSG_IMPLEMENTATION
#define MSG_NO_MALLOC
#include "elf.h"
#include <errno.h>
#include <fcntl.h>
#include <heap/heap.h>
#include <mango/log.h>
#include <mango/msg.h>
@@ -10,6 +13,7 @@
#include <mango/vm.h>
#include <rosetta/bootstrap.h>
#include <rosetta/fs.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -17,84 +21,204 @@
#include <sys/remote.h>
#include <unistd.h>
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);
}

138
sys/ld/queue.c Normal file
View File

@@ -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;
}

100
sys/ld/queue.h Normal file
View File

@@ -0,0 +1,100 @@
#ifndef QUEUE_H_
#define QUEUE_H_
#include <stdbool.h>
#include <string.h>
#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

22
sys/ld/resolve.c Normal file
View File

@@ -0,0 +1,22 @@
#include "elf.h"
#include <mango/log.h>
#include <stdint.h>
#include <stdio.h>
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;
}

7
sys/ld/resolve.h Normal file
View File

@@ -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