ld: implement an actual program loader
This commit is contained in:
47
sys/ld/arch/x86_64/resolve.S
Normal file
47
sys/ld/arch/x86_64/resolve.S
Normal 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
691
sys/ld/btree.c
Normal 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
475
sys/ld/btree.h
Normal 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
800
sys/ld/elf.c
Normal 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
365
sys/ld/elf.h
Normal 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
16
sys/ld/hash.c
Normal 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
8
sys/ld/hash.h
Normal 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
211
sys/ld/image-list.c
Normal 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
59
sys/ld/image-list.h
Normal 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
|
||||
244
sys/ld/main.c
244
sys/ld/main.c
@@ -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
138
sys/ld/queue.c
Normal 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
100
sys/ld/queue.h
Normal 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
22
sys/ld/resolve.c
Normal 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
7
sys/ld/resolve.h
Normal 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
|
||||
Reference in New Issue
Block a user