You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: tls.rs
    path of file: ./repos/rustix/src/backend/linux_raw/runtime
    
    the code of the file until where you have to start completion: //! TLS utilities.
//!
//! # Safety
//!
//! This file contains code that reads the raw phdr array pointed to by the
//! kernel-provided AUXV values.
#![allow(unsafe_code)]

use crate::backend::c;
use crate::backend::param::auxv::exe_phdrs;
use core::arch::global_asm;
use core::ptr::{null, NonNull};
use linux_raw_sys::elf::*;

/// For use with [`set_thread_area`].
///
/// [`set_thread_area`]: crate::runtime::set_thread_area
#[cfg(target_arch = "x86")]
pub type UserDesc = linux_raw_sys::general::user_desc;

pub(crate) fn startup_tls_info() -> StartupTlsInfo {
    let mut base = null();
    let mut tls_phdr = null();
    let mut stack_size = 0;

    let (first_phdr, phent, phnum) = exe_phdrs();
    let mut current_phdr = first_phdr.cast::<Elf_Phdr>();

    // The dynamic address of the dynamic section, which we can compare with
    // the `PT_DYNAMIC` header's static address, if present.
    let dynamic_addr: *const c::c_void = unsafe { &_DYNAMIC };

    // SAFETY: We assume the phdr array pointer and length the kernel provided
    // to the process describe a valid phdr array.
    unsafe {
        let phdrs_end = current_phdr.cast::<u8>().add(phnum * phent).cast();
        while current_phdr != phdrs_end {
            let phdr = &*current_phdr;
            current_phdr = current_phdr.cast::<u8>().add(phent).cast();

            match phdr.p_type {
                // Compute the offset from the static virtual addresses
                // in the `p_vaddr` fields to the dynamic addresses. We don't
                // always get a `PT_PHDR` or 
    
pub(crate) fn startup_tls_info() -> StartupTlsInfo {
    let mut base = null();
    let mut tls_phdr = null();
    let mut stack_size = 0;

    let (first_phdr, phent, phnum) = exe_phdrs();
    let mut current_phdr = first_phdr.cast::<Elf_Phdr>();

    // The dynamic address of the dynamic section, which we can compare with
    // the `PT_DYNAMIC` header's static address, if present.
    let dynamic_addr: *const c::c_void = unsafe { &_DYNAMIC };

    // SAFETY: We assume the phdr array pointer and length the kernel provided
    // to the process describe a valid phdr array.
    unsafe {
        let phdrs_end = current_phdr.cast::<u8>().add(phnum * phent).cast();
        while current_phdr != phdrs_end {
            let phdr = &*current_phdr;
            current_phdr = current_phdr.cast::<u8>().add(phent).cast();

            match phdr.p_type {
                // Compute the offset from the static virtual addresses
                // in the `p_vaddr` fields to the dynamic addresses. We don't
                // always get a `PT_PHDR` or `PT_DYNAMIC` header, so use
                // whichever one we get.
                PT_PHDR => base = first_phdr.cast::<u8>().wrapping_sub(phdr.p_vaddr),
                PT_DYNAMIC => base = dynamic_addr.cast::<u8>().wrapping_sub(phdr.p_vaddr),

                PT_TLS => tls_phdr = phdr,
                PT_GNU_STACK => stack_size = phdr.p_memsz,
                _ => {}
            }
        }

        if tls_phdr.is_null() {
            StartupTlsInfo {
                addr: NonNull::dangling().as_ptr(),
                mem_size: 0,
                file_size: 0,
                align: 1,
                stack_size: 0,
            }
        } else {
            StartupTlsInfo {
                addr: base.cast::<u8>().wrapping_add((*tls_phdr).p_vaddr).cast(),
                mem_size: (*tls_phdr).p_memsz,
                file_size: (*tls_phdr).p_filesz,
                align: (*tls_phdr).p_align,
                stack_size,
            }
        }
    }
}