MOS Source Code
Loading...
Searching...
No Matches
elf.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/tasks/elf.hpp"
4
6#include "mos/io/io.hpp"
7#include "mos/mm/mm.hpp"
8#include "mos/mm/mmap.hpp"
10#include "mos/syslog/printk.hpp"
11#include "mos/tasks/process.hpp"
14#include "mos/tasks/thread.hpp"
15
16#include <mos/types.hpp>
17#include <mos_stdlib.hpp>
18#include <mos_string.hpp>
19
20MOS_STATIC_ASSERT(sizeof(elf_header_t) == 0x40, "elf_header has wrong size");
21MOS_STATIC_ASSERT(sizeof(elf_program_hdr_t) == 0x38, "elf_program_header has wrong size");
22
23static void add_auxv_entry(auxv_vec_t *var, unsigned long type, unsigned long val)
24{
25 MOS_ASSERT_X(var->count < AUXV_VEC_SIZE, "auxv vector overflow, increase AUXV_VEC_SIZE");
26
27 var->vector[var->count].a_type = type;
28 var->vector[var->count].a_un.a_val = val;
29 var->count++;
30}
31
32static bool elf_verify_header(const elf_header_t *header)
33{
34 if (header->identity.magic[0] != ELFMAG0)
35 return false;
36
37 if (strncmp(&header->identity.magic[1], "ELF", 3) != 0)
38 return false;
39
40 if (header->identity.bits != ELFCLASS64)
41 return false;
42
44 return false;
45
46 if (header->identity.osabi != 0)
47 return false;
48
49 if (header->identity.version != EV_CURRENT)
50 return false;
51
52 if (header->machine_type != MOS_ELF_PLATFORM)
53 return false;
54
55 return true;
56}
57
58[[nodiscard]] static bool elf_read_file(file_t *file, void *buf, off_t offset, size_t size)
59{
60 const size_t read = io_pread(&file->io, buf, size, offset);
61 return read == size;
62}
63
65{
66 MOS_UNUSED(elf);
67 return 0x4000000; // TODO: randomize
68}
69
87
88static void elf_setup_main_thread(Thread *thread, elf_startup_info_t *const info, ptr_t *const out_pargv, ptr_t *const out_penvp)
89{
90 pr_dinfo2(elf, "cpu %d: setting up a new main thread %pt of process %pp", current_cpu->id, thread, thread->owner);
91
92 MOS_ASSERT_X(thread->u_stack.head == thread->u_stack.top, "thread %pt's user stack is not empty", thread);
93 stack_push_val(&thread->u_stack, (uintn) 0);
94
95 const void *stack_envp[info->envc + 1]; // +1 for the null terminator
96 const void *stack_argv[info->argc + 1]; // +1 for the null terminator
97
98 // calculate the size of entire stack usage
99 size_t stack_size = 0;
100 stack_size += sizeof(uintn); // the topmost zero
101 stack_size += strlen(info->invocation) + 1; // +1 for the null terminator
102
103 for (int i = 0; i < info->envc; i++)
104 stack_size += strlen(info->envp[i]) + 1; // +1 for the null terminator
105
106 for (int i = 0; i < info->argc; i++)
107 stack_size += strlen(info->argv[i]) + 1; // +1 for the null terminator
108
109 stack_size += sizeof(Elf64_auxv_t) * (info->auxv.count + 2); // AT_EXECFN and AT_NULL
110 stack_size += sizeof(stack_envp); // envp
111 stack_size += sizeof(stack_argv); // argv
112 stack_size += sizeof(uintn); // argc
113
114 // align to 16 bytes
115 const size_t aligned_stack_size = ALIGN_UP(stack_size, 16);
116 thread->u_stack.head = thread->u_stack.top - (aligned_stack_size - stack_size); // so that the stack can be aligned to 16 bytes
117
118 stack_push_val(&thread->u_stack, (uintn) 0);
119
120 void *invocation_ptr = stack_push(&thread->u_stack, info->invocation, strlen(info->invocation) + 1); // +1 for the null terminator
121
122 add_auxv_entry(&info->auxv, AT_EXECFN, (ptr_t) invocation_ptr);
123 add_auxv_entry(&info->auxv, AT_NULL, 0);
124
125 // ! copy the environment to the stack in reverse order !
126 if (info->envc == 0)
127 goto no_envp;
128
129 for (int i = info->envc - 1; i >= 0; i--)
130 {
131 const size_t len = strlen(info->envp[i]) + 1; // +1 for the null terminator
132 stack_envp[i] = stack_push(&thread->u_stack, info->envp[i], len);
133 }
134
135no_envp:
136 stack_envp[info->envc] = NULL;
137
138 // ! copy the argv to the stack in reverse order !
139 if (info->argc == 0)
140 goto no_argv;
141
142 for (int i = info->argc - 1; i >= 0; i--)
143 {
144 const size_t len = strlen(info->argv[i]) + 1; // +1 for the null terminator
145 stack_argv[i] = stack_push(&thread->u_stack, info->argv[i], len);
146 }
147
148no_argv:
149 stack_argv[info->argc] = NULL;
150
151 stack_push(&thread->u_stack, info->auxv.vector, sizeof(Elf64_auxv_t) * info->auxv.count); // auxv
152 *out_penvp = (ptr_t) stack_push(&thread->u_stack, &stack_envp, sizeof(char *) * (info->envc + 1)); // envp
153 *out_pargv = (ptr_t) stack_push(&thread->u_stack, &stack_argv, sizeof(char *) * (info->argc + 1)); // argv
154 stack_push_val(&thread->u_stack, (uintn) info->argc); // argc
155 MOS_ASSERT(thread->u_stack.head % 16 == 0);
156}
157
158static void elf_map_segment(const elf_program_hdr_t *const ph, ptr_t map_bias, MMContext *mm, file_t *file)
159{
161 pr_dinfo2(elf, "program header %c%c%c, type '%d' at " PTR_FMT, //
162 ph->p_flags & ELF_PF_R ? 'r' : '-', //
163 ph->p_flags & ELF_PF_W ? 'w' : '-', //
164 ph->p_flags & ELF_PF_X ? 'x' : '-', //
165 ph->header_type, //
166 ph->vaddr //
167 );
168
169 MOS_ASSERT(ph->data_offset % MOS_PAGE_SIZE == ph->vaddr % MOS_PAGE_SIZE); // offset ≡ vaddr (mod page size)
170 MOS_ASSERT_X(ph->size_in_file <= ph->size_in_mem, "invalid ELF: size in file is larger than size in memory");
171
172 const vm_flags flags = [pflags = ph->p_flags]()
173 {
174 vm_flags f = VM_USER;
175 if (pflags & ELF_PF_R)
176 f |= VM_READ;
177 if (pflags & ELF_PF_W)
178 f |= VM_WRITE;
179 if (pflags & ELF_PF_X)
180 f |= VM_EXEC;
181 return f;
182 }();
183
184 const ptr_t aligned_vaddr = ALIGN_DOWN_TO_PAGE(ph->vaddr);
185 const size_t npages = (ALIGN_UP_TO_PAGE(ph->vaddr + ph->size_in_mem) - aligned_vaddr) / MOS_PAGE_SIZE;
186 const size_t aligned_size = ALIGN_DOWN_TO_PAGE(ph->data_offset);
187
188 const ptr_t map_start = map_bias + aligned_vaddr;
189 pr_dinfo2(elf, " mapping %zu pages at " PTR_FMT " (bias at " PTR_FMT ") from offset %zu...", npages, map_start, map_bias, aligned_size);
190
191 const ptr_t vaddr = mmap_file(mm, map_start, mmap_flags_t(MMAP_PRIVATE | MMAP_EXACT), flags, npages, &file->io, aligned_size);
192 MOS_ASSERT_X(vaddr == map_start, "failed to map ELF segment at " PTR_FMT, aligned_vaddr);
193
194 if (ph->size_in_file < ph->size_in_mem)
195 {
196 pr_dinfo2(elf, " ... and zeroing %zu bytes at " PTR_FMT, ph->size_in_mem - ph->size_in_file, map_bias + ph->vaddr + ph->size_in_file);
197 memzero((char *) map_bias + ph->vaddr + ph->size_in_file, ph->size_in_mem - ph->size_in_file);
198 }
199
200 pr_dinfo2(elf, " ... done");
201}
202
203static ptr_t elf_map_interpreter(const char *path, MMContext *mm)
204{
205 auto interp_file = vfs_openat(AT_FDCWD, path, open_flags(OPEN_READ | OPEN_EXECUTE));
206 if (interp_file.isErr())
207 return 0;
208
209 io_ref(&interp_file->io);
210
211 elf_header_t elf;
212 if (!elf_read_and_verify_executable(interp_file.get(), &elf))
213 {
214 pr_emerg("failed to verify ELF header for '%s'", dentry_name(interp_file->dentry).c_str());
215 io_unref(&interp_file->io);
216 return 0;
217 }
218
219 ptr_t entry = 0;
220
221 for (size_t i = 0; i < elf.ph.count; i++)
222 {
224 if (!elf_read_file(interp_file.get(), &ph, elf.ph_offset + i * elf.ph.entry_size, elf.ph.entry_size))
225 {
226 pr_emerg("failed to read program header %zu for '%s'", i, dentry_name(interp_file->dentry).c_str());
227 io_unref(&interp_file->io);
228 return 0;
229 }
230
231 if (ph.header_type == ELF_PT_LOAD)
232 {
233 // interpreter is always loaded at vaddr 0
234 elf_map_segment(&ph, MOS_ELF_INTERPRETER_BASE_OFFSET, mm, interp_file.get());
235 entry = elf.entry_point;
236 }
237 }
238
239 io_unref(&interp_file->io);
240 return MOS_ELF_INTERPRETER_BASE_OFFSET + entry;
241}
242
244{
245 bool ret = true;
246
247 add_auxv_entry(&info->auxv, AT_PAGESZ, MOS_PAGE_SIZE);
248 add_auxv_entry(&info->auxv, AT_UID, 0);
249 add_auxv_entry(&info->auxv, AT_EUID, 0);
250 add_auxv_entry(&info->auxv, AT_GID, 0);
251 add_auxv_entry(&info->auxv, AT_EGID, 0);
253
254 // !! after this point, we must make sure that we switch back to the previous address space before returning from this function !!
255 MMContext *const prev_mm = mm_switch_context(proc->mm);
256
257 bool should_bias = elf.object_type == ET_DYN; // only ET_DYN (shared libraries) needs randomization
258 ptrdiff_t map_bias = 0; // ELF segments are loaded at vaddr + load_bias
259
260 bool has_interpreter = false;
261 ptr_t interp_entrypoint = 0;
262 ptr_t auxv_phdr_vaddr = false; // whether we need to add AT_PHDR, AT_PHENT, AT_PHNUM to the auxv vector
263
264 for (size_t i = 0; i < elf.ph.count; i++)
265 {
267 if (!elf_read_file(file, &ph, elf.ph_offset + i * elf.ph.entry_size, elf.ph.entry_size))
268 {
269 pr_emerg("failed to read program header %zu for '%s'", i, dentry_name(file->dentry).c_str());
270 const auto prev = mm_switch_context(prev_mm);
271 (void) prev;
272 return false;
273 }
274
275 switch (ph.header_type)
276 {
277 case ELF_PT_NULL: break; // ignore
278 case ELF_PT_INTERP:
279 {
280 char interp_name[ph.size_in_file];
281 if (!elf_read_file(file, interp_name, ph.data_offset, ph.size_in_file))
282 {
283 pr_emerg("failed to read interpreter name for '%s'", dentry_name(file->dentry).c_str());
284 const auto prev = mm_switch_context(prev_mm);
285 (void) prev;
286 return false;
287 }
288 pr_dinfo2(elf, "elf interpreter: %s", interp_name);
289 has_interpreter = true;
290 interp_entrypoint = elf_map_interpreter(interp_name, proc->mm);
291 if (!interp_entrypoint)
292 {
293 pr_dinfo2(elf, "failed to map interpreter '%s'", interp_name);
294 const auto prev = mm_switch_context(prev_mm);
295 (void) prev;
296 return false;
297 }
298
299 if (should_bias)
300 map_bias = elf_determine_loadbias(&elf);
301
302 break;
303 }
304 case ELF_PT_LOAD:
305 {
306 elf_map_segment(&ph, map_bias, proc->mm, file);
307 break;
308 }
309 case ELF_PT_PHDR:
310 {
311 auxv_phdr_vaddr = ph.vaddr;
312 break;
313 }
314
315 case ELF_PT_NOTE: break; // intentionally ignored
316 case ELF_PT_DYNAMIC: break; // will be handled by the dynamic linker
317 case ELF_PT_TLS: break; // will be handled by the dynamic linker or libc
318 default:
319 {
321 pr_dinfo2(elf, "ignoring OS-specific program header type 0x%x", ph.header_type);
323 pr_dinfo2(elf, "ignoring processor-specific program header type 0x%x", ph.header_type);
324 else
325 pr_warn("unknown program header type 0x%x", ph.header_type);
326 break;
327 }
328 };
329 }
330
331 if (auxv_phdr_vaddr)
332 {
333 add_auxv_entry(&info->auxv, AT_PHDR, map_bias + auxv_phdr_vaddr);
334 add_auxv_entry(&info->auxv, AT_PHENT, elf.ph.entry_size);
335 add_auxv_entry(&info->auxv, AT_PHNUM, elf.ph.count);
336 }
337
338 add_auxv_entry(&info->auxv, AT_ENTRY, map_bias + elf.entry_point); // the entry point of the executable, not the interpreter
339
340 ptr_t user_argv, user_envp;
341 const auto main_thread = proc->main_thread;
342 elf_setup_main_thread(main_thread, info, &user_argv, &user_envp);
343 platform_context_setup_main_thread(main_thread, has_interpreter ? interp_entrypoint : elf.entry_point, main_thread->u_stack.head, info->argc, user_argv, user_envp);
344
345 MMContext *prev = mm_switch_context(prev_mm);
346 MOS_UNUSED(prev);
347
348 return ret;
349}
350
352{
353 if (!elf_read_file(file, header, 0, sizeof(elf_header_t)))
354 return false;
355
356 const bool valid = elf_verify_header(header);
357 if (!valid)
358 return false;
359
360 if (header->object_type != ET_EXEC && header->object_type != ET_DYN)
361 return false;
362
363 return true;
364}
365
366bool elf_fill_process(Process *proc, file_t *file, const char *path, const char *const argv[], const char *const envp[])
367{
368 bool ret = false;
369
370 io_ref(&file->io);
371
372 elf_header_t elf;
373 if (!elf_read_and_verify_executable(file, &elf))
374 {
375 pr_emerg("failed to verify ELF header for '%s'", dentry_name(file->dentry).c_str());
376 io_unref(&file->io); // close the file, we should have the file's refcount == 0 here
377 return ret;
378 }
379
380 int argc = 0;
381 while (argv && argv[argc] != NULL)
382 argc++;
383
384 int envc = 0;
385 while (envp && envp[envc] != NULL)
386 envc++;
387
388 elf_startup_info_t info = {
389 .invocation = strdup(path),
390 .auxv = {},
391 .argc = argc,
392 .argv = kcalloc<const char *>(argc + 1),
393 .envc = envc,
394 .envp = kcalloc<const char *>(envc + 1),
395 };
396
397 for (int i = 0; i < argc; i++)
398 info.argv[i] = strdup(argv[i]); // copy the strings to kernel space, since we are switching to a new address space
399 info.argv[argc] = NULL;
400
401 for (int i = 0; i < envc; i++)
402 info.envp[i] = strdup(envp[i]); // copy the strings to kernel space, since we are switching to a new address space
403 info.envp[envc] = NULL;
404
405 ret = elf_do_fill_process(proc, file, elf, &info);
406
407 if (info.invocation)
408 kfree(info.invocation);
409 for (int i = 0; i < argc; i++)
410 kfree(info.argv[i]);
411 for (int i = 0; i < envc; i++)
412 kfree(info.envp[i]);
413 kfree(info.argv);
414 kfree(info.envp);
415
416 io_unref(&file->io); // close the file, we should have the file's refcount == 0 here
417 return ret;
418}
419
420Process *elf_create_process(const char *path, Process *parent, const char *const argv[], const char *const envp[], const stdio_t *ios)
421{
422 auto file = vfs_openat(AT_FDCWD, path, open_flags(OPEN_READ | OPEN_EXECUTE));
423 if (file.isErr())
424 {
425 mos_warn("failed to open '%s'", path);
426 return NULL;
427 }
428 io_ref(&file->io);
429
430 auto proc = process_new(parent, file->dentry->name, ios);
431 if (!proc)
432 {
433 mos_warn("failed to create process for '%s'", dentry_name(file->dentry).c_str());
434 io_unref(&file->io);
435 return proc;
436 }
437
438 const bool filled = elf_fill_process(proc, file.get(), path, argv, envp);
439 thread_complete_init(proc->main_thread);
440 scheduler_add_thread(proc->main_thread);
441
442 if (!filled)
443 {
444 // TODO how do we make sure that the process is cleaned up properly?
445 process_exit(std::move(proc), 0, SIGKILL);
446 proc = NULL;
447 }
448
449 io_unref(&file->io); // close the file, we should have the file's refcount == 0 here
450 return proc;
451}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.hpp:15
#define MOS_ASSERT(cond)
Definition assert.hpp:14
#define mos_warn(fmt,...)
Definition assert.hpp:23
#define MOS_PAGE_SIZE
Definition autoconf.h:6
#define MOS_ELF_INTERPRETER_BASE_OFFSET
Definition autoconf.h:21
const Char * c_str() const
Definition string.hpp:215
bool elf_fill_process(Process *proc, file_t *file, const char *path, const char *const argv[], const char *const envp[])
Definition elf.cpp:366
static ptr_t elf_determine_loadbias(elf_header_t *elf)
Definition elf.cpp:64
static void elf_map_segment(const elf_program_hdr_t *const ph, ptr_t map_bias, MMContext *mm, file_t *file)
Definition elf.cpp:158
Process * elf_create_process(const char *path, Process *parent, const char *const argv[], const char *const envp[], const stdio_t *ios)
Definition elf.cpp:420
bool elf_read_and_verify_executable(file_t *file, elf_header_t *header)
Definition elf.cpp:351
static ptr_t elf_map_interpreter(const char *path, MMContext *mm)
Definition elf.cpp:203
static bool elf_verify_header(const elf_header_t *header)
Definition elf.cpp:32
static void elf_setup_main_thread(Thread *thread, elf_startup_info_t *const info, ptr_t *const out_pargv, ptr_t *const out_penvp)
Definition elf.cpp:88
static bool elf_read_file(file_t *file, void *buf, off_t offset, size_t size)
Definition elf.cpp:58
__nodiscard bool elf_do_fill_process(Process *proc, file_t *file, elf_header_t elf, elf_startup_info_t *info)
Definition elf.cpp:243
static void add_auxv_entry(auxv_vec_t *var, unsigned long type, unsigned long val)
Definition elf.cpp:23
@ ELF_PT_PROCESSOR_LO
Definition elf.hpp:75
@ ELF_PT_PHDR
Definition elf.hpp:68
@ ELF_PT_DYNAMIC
Definition elf.hpp:64
@ ELF_PT_LOAD
Definition elf.hpp:63
@ ELF_PT_OS_HIGH
Definition elf.hpp:74
@ ELF_PT_OS_LOW
Definition elf.hpp:73
@ ELF_PT_PROCESSOR_HI
Definition elf.hpp:76
@ ELF_PT_NOTE
Definition elf.hpp:66
@ ELF_PT_INTERP
Definition elf.hpp:65
@ ELF_PT_NULL
Definition elf.hpp:62
@ ELF_PT_TLS
Definition elf.hpp:69
#define ELF_ENDIANNESS_MOS_DEFAULT
Definition elf.hpp:21
@ ELF_PF_X
Definition elf.hpp:81
@ ELF_PF_W
Definition elf.hpp:82
@ ELF_PF_R
Definition elf.hpp:83
#define AUXV_VEC_SIZE
Definition elf.hpp:98
open_flags
Definition fs_types.h:26
@ OPEN_READ
Definition fs_types.h:28
@ OPEN_EXECUTE
Definition fs_types.h:30
#define stack_push_val(stack, val)
Definition stack.hpp:26
MOSAPI void * stack_push(downwards_stack_t *stack, const void *data, size_t size)
Definition stack.cpp:32
MOSAPI char * strdup(const char *src)
MOSAPI s32 strncmp(const char *str1, const char *str2, size_t n)
MOSAPI void(1, 2) fatal_abort(const char *fmt
__nodiscard MMContext * mm_switch_context(MMContext *new_ctx)
Definition mm.cpp:120
PtrResult< file_t > vfs_openat(int fd, const char *path, open_flags flags)
Open a file at a given path.
Definition vfs.cpp:581
size_t io_pread(io_t *io, void *buf, size_t count, off_t offset)
Definition io.cpp:152
io_t * io_ref(io_t *io)
Definition io.cpp:74
io_t * io_unref(io_t *io)
Definition io.cpp:93
const char ** argv
Definition kmain.cpp:34
size_t argc
Definition kmain.cpp:33
mmap_flags_t
Definition mm_types.h:15
@ MMAP_PRIVATE
Definition mm_types.h:17
@ MMAP_EXACT
Definition mm_types.h:16
ptr_t mmap_file(MMContext *ctx, ptr_t hint_addr, mmap_flags_t flags, vm_flags vm_flags, size_t n_pages, io_t *io, off_t offset)
Map a file into the current process's address space.
Definition mmap.cpp:73
#define MOS_IN_RANGE(addr, start, end)
Definition mos_global.h:79
#define __nodiscard
Definition mos_global.h:35
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:76
#define ALIGN_DOWN_TO_PAGE(addr)
Definition mos_global.h:77
#define MOS_UNUSED(x)
Definition mos_global.h:65
#define MOS_STATIC_ASSERT
Definition mos_global.h:14
#define ALIGN_UP(addr, size)
Definition mos_global.h:74
#define NULL
Definition pb_syshdr.h:46
static size_t strlen(const char *s)
Definition pb_syshdr.h:80
vm_flags
Definition platform.hpp:42
@ VM_READ
Definition platform.hpp:44
@ VM_EXEC
Definition platform.hpp:46
@ VM_WRITE
Definition platform.hpp:45
@ VM_USER
Definition platform.hpp:48
#define current_cpu
Definition platform.hpp:31
#define pr_warn(fmt,...)
Definition printk.hpp:38
#define pr_emerg(fmt,...)
Definition printk.hpp:39
#define pr_dinfo2(feat, fmt,...)
Definition printk.hpp:27
void process_exit(Process *&&proc, u8 exit_code, signal_t signal)
Definition process.cpp:284
Process * process_new(Process *parent, mos::string_view name, const stdio_t *ios)
Definition process.cpp:137
#define MOS_ELF_PLATFORM
void platform_context_setup_main_thread(Thread *thread, ptr_t entry, ptr_t sp, int argc, ptr_t argv, ptr_t envp)
#define memzero(ptr, size)
void scheduler_add_thread(Thread *thread)
Add a thread to the scheduler, so that it can be scheduled.
Definition schedule.cpp:78
size_t size
Definition slab.cpp:34
Thread * main_thread
mos::string name
MMContext * mm
Process * owner
downwards_stack_t u_stack
user-mode stack
Elf64_auxv_t vector[16]
Definition elf.hpp:103
int count
Definition elf.hpp:102
elf_identity_t identity
Definition elf.hpp:39
int machine_type
Definition elf.hpp:41
int object_type
Definition elf.hpp:40
u16 count
Definition elf.hpp:54
size_t ph_offset
Definition elf.hpp:46
struct elf_header_t::@372374123200070342027241342357315321025311300122 ph
ptr_t entry_point
Definition elf.hpp:45
u16 entry_size
Definition elf.hpp:54
char magic[4]
Definition elf.hpp:26
u32 version
Definition elf.hpp:29
u32 endianness
Definition elf.hpp:28
elf_program_header_type header_type
Definition elf.hpp:88
ptr_t size_in_mem
Definition elf.hpp:94
elf_ph_flags p_flags
Definition elf.hpp:89
ptr_t size_in_file
Definition elf.hpp:93
ptr_t data_offset
Definition elf.hpp:90
auxv_vec_t auxv
Definition elf.hpp:109
const char ** envp
Definition elf.hpp:114
const char ** argv
Definition elf.hpp:111
const char * invocation
Definition elf.hpp:108
io_t io
dentry_t * dentry
A wrapper type for the standard I/O streams.
Definition process.hpp:17
Thread * thread_complete_init(Thread *thread)
Definition thread.cpp:165
ssize_t off_t
Definition types.h:80
unsigned long uintn
Definition types.h:26
#define PTR_FMT
Definition types.h:29
unsigned long ptr_t
Definition types.h:21
mos::string dentry_name(const dentry_t *dentry)