getauxval 无法与 ifunc 配合使用

问题描述 投票:0回答:1

这个问题源自here,然而,它仍然是一个全新的问题。

在提到的问题之后,我有以下代码。可以通过

memcpy
my_memcpy
use_embed
之间进行选择。

#include <stdio.h>
#include <string.h>
static int use_embed = 1;
void *my_memcpy (void *dst, const void *src, size_t len)
{
    printf("aaaaa\n");
    return dst;
}
static void * (*resolve_memcpy (void))(void *, const void *, size_t)
{
    if(use_embed == 1) {
        return memcpy;
    } else {
        return my_memcpy;
    }
}
void *memcpy2 (void *, const void *, size_t) __attribute__ ((ifunc ("resolve_memcpy")));
#define memcpy memcpy2
// extern void *memcpy (void *, const void *, size_t);
int main() {
    int source = 5;
    int dest = 6;
    memcpy(&dest, &source, sizeof(source));
    printf("%d %d\n", use_embed, dest);
    return 0;
}

但是,我想用

getauxval
来决定使用哪个版本,所以我有以下代码


#include <stdio.h>
#include <string.h>
#include <sys/auxv.h>

#ifndef HWCAP2_MTE
#define HWCAP2_MTE (1 << 18)
#endif
#ifndef HWCAP_SVE
#define HWCAP_SVE (1 << 22)
#endif
#ifndef AT_HWCAP2
#define AT_HWCAP2 26
#endif
#ifndef AT_HWCAP
#define AT_HWCAP 16
#endif
/// check if MTE is supported in current environment
static int mte_supported(void)
{
    return (getauxval(AT_HWCAP2) & HWCAP2_MTE);
}
/// check if SVE is supported in current environment
static int sve_supported(void)
{
    return (getauxval(AT_HWCAP) & HWCAP_SVE);
}

void *my_memcpy (void *dst, const void *src, size_t len)
{
    printf("aaaaa\n");
    return dst;
}
static void * (*resolve_memcpy (void))(void *, const void *, size_t)
{
    if(mte_supported() != 0) {
        return my_memcpy;
    }
    if(sve_supported() != 0) {
        return my_memcpy;
    }
    return my_memcpy;
}
void *memcpy2 (void *, const void *, size_t) __attribute__ ((ifunc ("resolve_memcpy")));
#define memcpy memcpy2
// extern void *memcpy (void *, const void *, size_t);
int main() {
    int source = 5;
    int dest = 6;
    memcpy2(&dest, &source, sizeof(source));
    printf("%d\n", dest);
    return 0;
}

使用 clang15 构建时会出现段错误,原因与我上一个问题中提到的相同(与 PLT 相关)。详细来说,PLT 部分中

getauxval@plt
的第一个跳转既不指向
_dl_runtime_resolve
,也不指向
getauxval
的读取访问点。

我的问题是:

  1. 如何修复代码?我的意思是
    ifunc
    应该预见到这个问题。它一定已经提供了一些我不知道的解决方案。
  2. 如果
    ifunc
    没有解决方案,我们在PLT级别上有一些解决方法吗?比如
    LD_BIND_NOW
    之类的。

====编辑====

clang --version
clang version 15.0.7 (https://github.com/llvm/llvm-project 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a)
Target: x86_64-unknown-linux-gnu
Thread model: posix

cat /proc/version
Linux version 5.14.0-162.6.1.el9_1.x86_64 ([email protected]) (gcc (GCC) 11.3.1 20220421 (Red Hat 11.3.1-2), GNU ld version 2.35.2-24.el9) #1 SMP PREEMPT_DYNAMIC Fri Nov 18 02:06:38 UTC 2022
c linux linker clang
1个回答
0
投票

我也遇到了这个问题,并在System V ABI for AArch64中找到了解决方案:解析器函数实际上将 hwcaps 作为参数传递,从而无需调用

getauxval
(正如您所发现的那样,这不是'在调用解析器时不一定安全)。

这里对您的代码进行了修改以演示这一点:

#include <stdio.h>
#include <string.h>
#include <sys/auxv.h>

#ifndef HWCAP2_MTE
#define HWCAP2_MTE (1 << 18)
#endif
#ifndef HWCAP_SVE
#define HWCAP_SVE (1 << 22)
#endif
/// check if MTE is supported in current environment
static int mte_supported(unsigned long hwcap2)
{
    return (hwcap2 & HWCAP2_MTE);
}
/// check if SVE is supported in current environment
static int sve_supported(unsigned long hwcap)
{
    return (hwcap & HWCAP_SVE);
}

void *my_memcpy_mte(void *dst, const void *src, size_t len)
{
    printf("MTE\n");
    return dst;
}

void *my_memcpy_sve(void *dst, const void *src, size_t len)
{
    printf("SVE\n");
    return dst;
}

void *my_memcpy(void *dst, const void *src, size_t len)
{
    printf("fallback\n");
    return dst;
}

typedef struct
{
    unsigned long size;
    unsigned long hwcap;
    unsigned long hwcap2;
} ifunc_arg;

static void * (*resolve_memcpy (unsigned long hwcap, const ifunc_arg *extra))(void *, const void *, size_t)
{
    if ((hwcap & (1ULL << 62)) && mte_supported(extra->hwcap2) != 0) {
        return my_memcpy_mte;
    }
    if(sve_supported(hwcap) != 0) {
        return my_memcpy_sve;
    }
    return my_memcpy;
}
void *memcpy2 (void *, const void *, size_t) __attribute__ ((ifunc ("resolve_memcpy")));
#define memcpy memcpy2
// extern void *memcpy (void *, const void *, size_t);
int main() {
    int source = 5;
    int dest = 6;
    memcpy2(&dest, &source, sizeof(source));
    printf("%d\n", dest);
    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.