loader64.asm 3.96 KB
Newer Older
PoroCYon's avatar
PoroCYon committed
1
2
; vim: set ft=nasm:

3
4
;%define R10_BIAS (0x2B4)
%define R10_BIAS (0x2B4+0x40)
5

PoroCYon's avatar
PoroCYon committed
6
7
8
9
10
11
12
13
14
%include "rtld.inc"

%ifdef ELF_TYPE
[section .text.startup.smol]
%else
; not defined -> debugging!
[section .text]
%endif

15
; r9 : ptrdiff_t glibc_vercompat_extra_hi_field_off
PoroCYon's avatar
PoroCYon committed
16
17
; r10: struct link_map* entry + far correction factor
; r12: struct link_map* entry
18
; r14: struct link_map* root
PoroCYon's avatar
PoroCYon committed
19
; r13: _dl_fini address (reqd by the ABI)
PoroCYon's avatar
PoroCYon committed
20
21
22
23
24
25
26

%ifndef ELF_TYPE
extern _symbols
global _start
_start:
%endif
_smol_start:
PoroCYon's avatar
PoroCYon committed
27
%ifdef USE_DL_FINI
PoroCYon's avatar
PoroCYon committed
28
   xchg r13, rdx ; _dl_fini
PoroCYon's avatar
PoroCYon committed
29
%endif
PoroCYon's avatar
PoroCYon committed
30

PoroCYon's avatar
PoroCYon committed
31
32
%ifdef USE_DT_DEBUG
    mov r12, [rel _DEBUG]
33
    mov r12, [r12 + 8]
PoroCYon's avatar
PoroCYon committed
34
%else
PoroCYon's avatar
PoroCYon committed
35
    mov r12, [rsp -  8]        ; return address of _dl_init
36
37
    mov ebx, dword [r12 - 20] ; decode part of 'mov rdi, [rel _rtld_global]'
    mov r12, [r12 + rbx - 16]  ; ???
PoroCYon's avatar
PoroCYon committed
38
%endif
39
        ; struct link_map* root = r12
PoroCYon's avatar
PoroCYon committed
40
41
%ifdef SKIP_ENTRIES
    mov r12, [r12 + L_NEXT_OFF] ; skip this binary
42
43
;   mov r12, [r12 + L_NEXT_OFF] ; skip the vdso
        ; the second one isn't needed anymore, see code below (.next_link)
PoroCYon's avatar
PoroCYon committed
44
45
%endif

46
47
       push _smol_start
       push r12
48
49
       push -1
        pop rcx
50
51
52
        pop rdi
        pop rax
repne scasd ; technically, scasq should be used, but ehhhh
53
        sub rdi, r12
54
55
        sub rdi, LF_ENTRY_OFF+4
       xchg r9, rdi
PoroCYon's avatar
PoroCYon committed
56

57
58
   push _symbols
        ; back up link_map root
59
   push r12
60
61
62
63
    pop r11
    pop rdi

;.loopme: jmp short .loopme ; debugging
64
    .next_hash:
65
66
67
68
69
70
71
        mov r14d, dword [rdi]
            ; assume we need at least one function
;        or al, al
;        jz short .needed_end
        mov r12, r11
;      push r11
       push r14
72
        pop rbx
73
;       pop r12
74
75
76
77
78
79
            ; shift left because we don't want to compare the lowest bit
        shr ebx, 1

        .next_link:
            mov r12, [r12 + L_NEXT_OFF]

80
            lea r10, [r12 + r9 + R10_BIAS]
81
82
83
84
85
86
87
88
89
                ; uint32_t bkt_ind(edx) = hash % entry->l_nbuckets
            xor edx, edx
           push r14
            pop rax
            mov ecx, dword [r10 + LF_NBUCKETS_OFF - R10_BIAS]
            div ecx

                ; uint32_t bucket(edx) = entry->l_gnu_buckets[bkt_ind]
            mov r8 , [r10 + LF_GNU_BUCKETS_OFF - R10_BIAS]
90
            mov ecx, dword [r8 + rdx * 4]
91

92
93
                ; can be ignored apparently?
;         jecxz .next_link
94
95
96

            .next_chain:
                    ; uint32_t luhash(ecx) = entry->l_gnu_chain_zero[bucket] >> 1
97
98
                mov rdx, [r10 + LF_GNU_CHAIN_ZERO_OFF - R10_BIAS]
                mov edx, dword [rdx + rcx * 4]
99

100
101
                    ; TODO: make this not suck. (maybe using bt*?)
                mov al, dl
102

103
                shr edx, 1
104
                    ; if (luhash == hash) break;
105
                cmp edx, ebx
106
107
108
109
110
                 je short .chain_break

                    ; ++bucket; } while (luhash & 1);
                and al, 1
                jnz short .next_link
111
112

                inc ecx
113
                jmp short .next_chain
PoroCYon's avatar
PoroCYon committed
114
115

        .chain_break:
116
117
118
119
120
121
122
123
124
                ; ElfW(Sym)* symtab = entry->l_info[DT_SYMTAB]->d_un.d_ptr
                ; ElfW(Sym)* sym = &symtab[bucket]
                ; *phash = sym->st_value + entry->l_addr

                ; ElfW(Dyn)* dyn(rax) = entry->l_info[DT_SYMTAB]
            mov rax, [r12 + L_INFO_DT_SYMTAB_OFF]
                ; ElfW(Sym)* symtab(rax) = dyn->d_un.d_ptr
            mov rax, [rax + D_UN_PTR_OFF]
                ; ElfW(Addr) symoff(rax) = symtab[bucket].st_value
125
            lea rdx, [rcx + rcx * 2]
126
127
            mov rax, [rax + rdx * 8 + ST_VALUE_OFF]
                ; void* finaladdr(rax) = symoff + entry->l_addr
128
            add rax, [r12 + L_ADDR_OFF]
129
130
131

                ; *phash = finaladdr
          stosq
PoroCYon's avatar
PoroCYon committed
132
            cmp word [rdi], 0
133
            jne short .next_hash
134
            ; } while (1)
135
;       jmp short .next_hash
PoroCYon's avatar
PoroCYon committed
136
137

.needed_end:
138
139
140
141
;  int3 ; debugging
;   xor rbp, rbp ; still 0 from _dl_start_user
%ifndef NO_START_ARG
        ; arg for _start
PoroCYon's avatar
PoroCYon committed
142
    mov rdi, rsp
143
%endif
144
%ifdef ALIGN_STACK
PoroCYon's avatar
PoroCYon committed
145
   push rax
146
%endif
PoroCYon's avatar
PoroCYon committed
147
%ifdef USE_DL_FINI
PoroCYon's avatar
PoroCYon committed
148
   xchg rsi, r13 ; _dl_fini
PoroCYon's avatar
PoroCYon committed
149
%endif
PoroCYon's avatar
PoroCYon committed
150
151
        ; fallthru to _start