这个问题源自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
的读取访问点。
我的问题是:
ifunc
应该预见到这个问题。它一定已经提供了一些我不知道的解决方案。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
我也遇到了这个问题,并在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;
}