(root)/
glibc-2.38/
elf/
dl-hwcaps.c
       1  /* Hardware capability support for run-time dynamic loader.
       2     Copyright (C) 2012-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <assert.h>
      20  #include <elf.h>
      21  #include <errno.h>
      22  #include <libintl.h>
      23  #include <unistd.h>
      24  #include <ldsodefs.h>
      25  
      26  #include <dl-procinfo.h>
      27  #include <dl-hwcaps.h>
      28  
      29  /* This is the result of counting the substrings in a colon-separated
      30     hwcaps string.  */
      31  struct hwcaps_counts
      32  {
      33    /* Number of substrings.  */
      34    size_t count;
      35  
      36    /* Sum of the individual substring lengths (without separators or
      37       null terminators).  */
      38    size_t total_length;
      39  
      40    /* Maximum length of an individual substring.  */
      41    size_t maximum_length;
      42  };
      43  
      44  /* Update *COUNTS according to the contents of HWCAPS.  Skip over
      45     entries whose bit is not set in MASK.  */
      46  static void
      47  update_hwcaps_counts (struct hwcaps_counts *counts, const char *hwcaps,
      48  		      uint32_t bitmask, const char *mask)
      49  {
      50    struct dl_hwcaps_split_masked sp;
      51    _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask);
      52    while (_dl_hwcaps_split_masked (&sp))
      53      {
      54        ++counts->count;
      55        counts->total_length += sp.split.length;
      56        if (sp.split.length > counts->maximum_length)
      57  	counts->maximum_length = sp.split.length;
      58      }
      59  }
      60  
      61  /* State for copy_hwcaps.  Must be initialized to point to
      62     the storage areas for the array and the strings themselves.  */
      63  struct copy_hwcaps
      64  {
      65    struct r_strlenpair *next_pair;
      66    char *next_string;
      67  };
      68  
      69  /* Copy HWCAPS into the string pairs and strings, advancing *TARGET.
      70     Skip over entries whose bit is not set in MASK.  */
      71  static void
      72  copy_hwcaps (struct copy_hwcaps *target, const char *hwcaps,
      73  	     uint32_t bitmask, const char *mask)
      74  {
      75    struct dl_hwcaps_split_masked sp;
      76    _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask);
      77    while (_dl_hwcaps_split_masked (&sp))
      78      {
      79        target->next_pair->str = target->next_string;
      80        char *slash = __mempcpy (__mempcpy (target->next_string,
      81  					  GLIBC_HWCAPS_PREFIX,
      82  					  strlen (GLIBC_HWCAPS_PREFIX)),
      83  			       sp.split.segment, sp.split.length);
      84        *slash = '/';
      85        target->next_pair->len
      86  	= strlen (GLIBC_HWCAPS_PREFIX) + sp.split.length + 1;
      87        ++target->next_pair;
      88        target->next_string = slash + 1;
      89      }
      90  }
      91  
      92  struct dl_hwcaps_priority *_dl_hwcaps_priorities;
      93  uint32_t _dl_hwcaps_priorities_length;
      94  
      95  /* Allocate _dl_hwcaps_priorities and fill it with data.  */
      96  static void
      97  compute_priorities (size_t total_count, const char *prepend,
      98  		    uint32_t bitmask, const char *mask)
      99  {
     100    _dl_hwcaps_priorities = malloc (total_count
     101  				  * sizeof (*_dl_hwcaps_priorities));
     102    if (_dl_hwcaps_priorities == NULL)
     103      _dl_signal_error (ENOMEM, NULL, NULL,
     104  		      N_("cannot create HWCAP priorities"));
     105    _dl_hwcaps_priorities_length = total_count;
     106  
     107    /* First the prepended subdirectories.  */
     108    size_t i = 0;
     109    {
     110      struct dl_hwcaps_split sp;
     111      _dl_hwcaps_split_init (&sp, prepend);
     112      while (_dl_hwcaps_split (&sp))
     113        {
     114  	_dl_hwcaps_priorities[i].name = sp.segment;
     115  	_dl_hwcaps_priorities[i].name_length = sp.length;
     116  	_dl_hwcaps_priorities[i].priority = i + 1;
     117  	++i;
     118        }
     119    }
     120  
     121    /* Then the built-in subdirectories that are actually active.  */
     122    {
     123      struct dl_hwcaps_split_masked sp;
     124      _dl_hwcaps_split_masked_init (&sp, _dl_hwcaps_subdirs, bitmask, mask);
     125      while (_dl_hwcaps_split_masked (&sp))
     126        {
     127  	_dl_hwcaps_priorities[i].name = sp.split.segment;
     128  	_dl_hwcaps_priorities[i].name_length = sp.split.length;
     129  	_dl_hwcaps_priorities[i].priority = i + 1;
     130  	++i;
     131        }
     132    }
     133    assert (i == total_count);
     134  }
     135  
     136  /* Sort the _dl_hwcaps_priorities array by name.  */
     137  static void
     138  sort_priorities_by_name (void)
     139  {
     140    /* Insertion sort.  There is no need to link qsort into the dynamic
     141       loader for such a short array.  */
     142    for (size_t i = 1; i < _dl_hwcaps_priorities_length; ++i)
     143      for (size_t j = i; j > 0; --j)
     144        {
     145  	struct dl_hwcaps_priority *previous = _dl_hwcaps_priorities + j - 1;
     146  	struct dl_hwcaps_priority *current = _dl_hwcaps_priorities + j;
     147  
     148  	/* Bail out if current is greater or equal to the previous
     149  	   value.  */
     150  	uint32_t to_compare;
     151  	if (current->name_length < previous->name_length)
     152  	  to_compare = current->name_length;
     153  	else
     154  	  to_compare = previous->name_length;
     155  	int cmp = memcmp (current->name, previous->name, to_compare);
     156  	if (cmp > 0
     157  	    || (cmp == 0 && current->name_length >= previous->name_length))
     158  	  break;
     159  
     160  	/* Swap *previous and *current.  */
     161  	struct dl_hwcaps_priority tmp = *previous;
     162  	*previous = *current;
     163  	*current = tmp;
     164        }
     165  }
     166  
     167  /* Return an array of useful/necessary hardware capability names.  */
     168  const struct r_strlenpair *
     169  _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
     170  		      const char *glibc_hwcaps_mask,
     171  		      size_t *sz, size_t *max_capstrlen)
     172  {
     173    /* glibc-hwcaps subdirectories.  */
     174    uint32_t hwcaps_subdirs_active = _dl_hwcaps_subdirs_active ();
     175    struct hwcaps_counts hwcaps_counts =  { 0, };
     176    update_hwcaps_counts (&hwcaps_counts, glibc_hwcaps_prepend, -1, NULL);
     177    update_hwcaps_counts (&hwcaps_counts, _dl_hwcaps_subdirs,
     178  			hwcaps_subdirs_active, glibc_hwcaps_mask);
     179    compute_priorities (hwcaps_counts.count, glibc_hwcaps_prepend,
     180  		      hwcaps_subdirs_active, glibc_hwcaps_mask);
     181    sort_priorities_by_name ();
     182  
     183    /* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix
     184       and a "/" suffix once stored in the result.  */
     185    hwcaps_counts.maximum_length += strlen (GLIBC_HWCAPS_PREFIX) + 1;
     186    size_t total = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1)
     187  		  + hwcaps_counts.total_length);
     188  
     189    *sz = hwcaps_counts.count + 1;
     190  
     191    /* This is the overall result.  */
     192    struct r_strlenpair *overall_result
     193      = malloc (*sz * sizeof (*overall_result) + total);
     194    if (overall_result == NULL)
     195      _dl_signal_error (ENOMEM, NULL, NULL,
     196  		      N_("cannot create capability list"));
     197  
     198    /* Fill in the glibc-hwcaps subdirectories.  */
     199    {
     200      struct copy_hwcaps target;
     201      target.next_pair = overall_result;
     202      target.next_string = (char *) (overall_result + *sz);
     203      copy_hwcaps (&target, glibc_hwcaps_prepend, -1, NULL);
     204      copy_hwcaps (&target, _dl_hwcaps_subdirs,
     205  		 hwcaps_subdirs_active, glibc_hwcaps_mask);
     206  
     207      /* Append an empty entry for the base directory itself.  */
     208      target.next_pair->str = target.next_string;
     209      target.next_pair->len = 0;
     210    }
     211  
     212    /* The maximum string length.  */
     213    *max_capstrlen = hwcaps_counts.maximum_length;
     214  
     215    return overall_result;
     216  }