From patchwork Tue Sep 5 15:12:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hengqi Chen X-Patchwork-Id: 13376598 X-Patchwork-Delegate: bpf@iogearbox.net Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ADDB628E7 for ; Thu, 7 Sep 2023 16:13:45 +0000 (UTC) Received: from mail-oi1-f174.google.com (mail-oi1-f174.google.com [209.85.167.174]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F224261AF for ; Thu, 7 Sep 2023 09:13:19 -0700 (PDT) Received: by mail-oi1-f174.google.com with SMTP id 5614622812f47-3a9f88b657eso725883b6e.3 for ; Thu, 07 Sep 2023 09:13:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1694102821; x=1694707621; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UsB9C/kF+PpX2LmrpmF5uLLoJCG5xKVOO9GLDkobJOA=; b=lLhckt5kkFBwdzCcWTa9Cj40folVI9GEWC/fJ9tOHTWXc5tAu9u/CowIxmKOy3p5M0 wY656g3PMlON5V2GvPUdN1cNThn0pyAgGmtj7zKu8n/nayS1hxiFpN8B8Ey85j3rrQMC 5GHd+3D7rjlYzoL3bFwo+Jhcy0VxACTNMnnLPISK4LjYzdeO1ZIT9PSBeo8VyBn5sBab yDxMx9Y6v9hikhBLFzBYaN4VWeNo4Kr4hJtFhe2OvBxodh0TJBSaap5xAv87oN43Nk0F Xdb87H76ex1ZiQvpaJR6Wfo+A0vZcCRh2QZeuWLhCWXIrgby74WmZbt5fYCkV1r6qjY+ 7Nkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1694102821; x=1694707621; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UsB9C/kF+PpX2LmrpmF5uLLoJCG5xKVOO9GLDkobJOA=; b=FAPS7EF5YUgqa/kouYQpkxHwuEWIKnkZ3KarRZU1l6Kqs2DbhNuNPsdpk3v+3B6DN0 zDDyyWmKF1EXZN2Zmik2aB/666i1KBYhGFVZOsI+1UUeKQ8ImvBRuZ5Qj4X+cDSS9y63 XDACrCArJ3x31uH5Q444sNoaHhtMm1DKgzVIA4dc/knLmmEl8P/iSjuxztfvX3Fm+NBV 5/fBt1TcsN8RMM4sny/vR6tBc2x0TZyJ0XpgQ0SlHYmMo7ACs6IMm8D47HhDzbyiE2qk JjV/0Rk7FmAfWxpkahY6o9KHLjbUPycd0uGT0PJP4er1hiWoEEDyvSWOGWF/iaJcFEmC zVdQ== X-Gm-Message-State: AOJu0YwZzn90CP2oth198RtZ0NJIRS5ELDKzDTmYC9LjYMz/ED+Hmnn8 ql7RzBHos7AD1pSRNE4nptaJrxBmCRUfpA== X-Google-Smtp-Source: AGHT+IHagdcCIT/Q9HQaXL/05O4ztQLLIcEjvE+bmfzJKyvjUsTu3HKOCH2yu3ISbYPIXd9+AgbVDw== X-Received: by 2002:a05:6870:a924:b0:1c3:91b9:e1e7 with SMTP id eq36-20020a056870a92400b001c391b9e1e7mr22496171oab.21.1694065405063; Wed, 06 Sep 2023 22:43:25 -0700 (PDT) Received: from ubuntu.. ([43.132.98.117]) by smtp.googlemail.com with ESMTPSA id q7-20020a637507000000b00570574feda0sm11860963pgc.19.2023.09.06.22.43.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Sep 2023 22:43:24 -0700 (PDT) From: Hengqi Chen To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, alan.maguire@oracle.com, olsajiri@gmail.com, hengqi.chen@gmail.com Subject: [PATCH bpf-next v2 1/3] libbpf: Resolve symbol conflicts at the same offset for uprobe Date: Tue, 5 Sep 2023 15:12:55 +0000 Message-Id: <20230905151257.729192-2-hengqi.chen@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230905151257.729192-1-hengqi.chen@gmail.com> References: <20230905151257.729192-1-hengqi.chen@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-0.8 required=5.0 tests=BAYES_00,DATE_IN_PAST_24_48, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: bpf@iogearbox.net Dynamic symbols in shared library may have the same name, for example: $ nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep rwlock_wrlock 000000000009b1a0 T __pthread_rwlock_wrlock@GLIBC_2.2.5 000000000009b1a0 T pthread_rwlock_wrlock@@GLIBC_2.34 000000000009b1a0 T pthread_rwlock_wrlock@GLIBC_2.2.5 $ readelf -W --dyn-syms /lib/x86_64-linux-gnu/libc.so.6 | grep rwlock_wrlock 706: 000000000009b1a0 878 FUNC GLOBAL DEFAULT 15 __pthread_rwlock_wrlock@GLIBC_2.2.5 2568: 000000000009b1a0 878 FUNC GLOBAL DEFAULT 15 pthread_rwlock_wrlock@@GLIBC_2.34 2571: 000000000009b1a0 878 FUNC GLOBAL DEFAULT 15 pthread_rwlock_wrlock@GLIBC_2.2.5 Currently, users can't attach a uprobe to pthread_rwlock_wrlock because there are two symbols named pthread_rwlock_wrlock and both are global bind. And libbpf considers it as a conflict. Since both of them are at the same offset we could accept one of them harmlessly. Note that we already does this in elf_resolve_syms_offsets. Signed-off-by: Hengqi Chen Reviewed-by: Alan Maguire Acked-by: Jiri Olsa --- tools/lib/bpf/elf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) -- 2.34.1 diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c index 9d0296c1726a..5c9e588b17da 100644 --- a/tools/lib/bpf/elf.c +++ b/tools/lib/bpf/elf.c @@ -214,7 +214,10 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name) if (ret > 0) { /* handle multiple matches */ - if (last_bind != STB_WEAK && cur_bind != STB_WEAK) { + if (elf_sym_offset(sym) == ret) { + /* same offset, no problem */ + continue; + } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) { /* Only accept one non-weak bind. */ pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n", sym->name, name, binary_path); From patchwork Tue Sep 5 15:12:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hengqi Chen X-Patchwork-Id: 13376724 X-Patchwork-Delegate: bpf@iogearbox.net Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E5BD913AE4 for ; Thu, 7 Sep 2023 17:31:53 +0000 (UTC) Received: from mail-qv1-xf2b.google.com (mail-qv1-xf2b.google.com [IPv6:2607:f8b0:4864:20::f2b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DEC7B10CA for ; Thu, 7 Sep 2023 10:31:29 -0700 (PDT) Received: by mail-qv1-xf2b.google.com with SMTP id 6a1803df08f44-64b3ae681d1so6982726d6.0 for ; Thu, 07 Sep 2023 10:31:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1694107836; x=1694712636; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DlLlKmiw/xSmwYWp60s46u5j6Sdvf+aKxz4mWiOlM+4=; b=DXQ3FcfBV8DFZYC9TiYJw8U/TOzhY5kH3X3ruPZaJFE7xYYHQlWJy9p2NGFVzJslOE s1w2Csvg5GpCgGu4sGRY5OqrnC4gKI/46Z8kSFRguVW0IdymO9n88hV+A9oGNRuRRgFm ffkuG9rbn7lk2iWlD6J1Ebe4ZTdWbmpFU0Hm63wf/cCO8ho6iypW0QGeFABSghWf4aWf Ebu67dTemu9dgddL3nrITkB+aN2Pc6Ha4cahHxC6wRDjYg0LtsFukhLDtRI9vcKjascP WghGCSlydGyT4K1iPE5snk+I9VqUdynVZF0NB/GWNpbjjMhCE4amWVefIkY+HCflsGpV I5xQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1694107836; x=1694712636; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DlLlKmiw/xSmwYWp60s46u5j6Sdvf+aKxz4mWiOlM+4=; b=Ong/ksqtcCHy1I76olk0XozpVK8Zx+1PinD7kGDs6FP97h7VLsUr9DfB19T80BofU5 BxIsWWmc9EFHCAaWg+/cpH2FCwcfcZhreIsPNatEmPlYPlk1ACz3n/rxjOx7+FjxEMH3 tolRE1C2BDBFVi3sBkbQbqkcnVObsBmmTMxPixmhUZoDeGcxLTjudb7RVdErnAStEnSd in9GkL+CBAEOGtt2XNoMIAq1YgSLFXX6BWtxuqeuHrABPJ3ht8eR7+X5vRevyj+zRy6x 3sPJpozZgEJ5vhY/+Gh0rcdwXqmjM8DUomvPd84Au/zcQ9rouAjfuDexNuZGwmnE7s8D c+PA== X-Gm-Message-State: AOJu0YzwWH/Jj3yKYh7Q+bD0rAkJDU4KFlt7uKpdozZex5UCJ/xWBx/8 t7ITb4BeAaPL+vqo8Fi1J76AO269pHa8zw== X-Google-Smtp-Source: AGHT+IEk+eHS3hjNC6DRpcHVKsC9lOqa8UoaqfGyCKZU+9pVgJ4xnj2TOwcoxU/z2edR4FVo+9t7Kw== X-Received: by 2002:a05:6870:d69f:b0:1ba:6180:ff47 with SMTP id z31-20020a056870d69f00b001ba6180ff47mr20503924oap.21.1694065417833; Wed, 06 Sep 2023 22:43:37 -0700 (PDT) Received: from ubuntu.. ([43.132.98.117]) by smtp.googlemail.com with ESMTPSA id q7-20020a637507000000b00570574feda0sm11860963pgc.19.2023.09.06.22.43.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Sep 2023 22:43:37 -0700 (PDT) From: Hengqi Chen To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, alan.maguire@oracle.com, olsajiri@gmail.com, hengqi.chen@gmail.com Subject: [PATCH bpf-next v2 2/3] libbpf: Support symbol versioning for uprobe Date: Tue, 5 Sep 2023 15:12:56 +0000 Message-Id: <20230905151257.729192-3-hengqi.chen@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230905151257.729192-1-hengqi.chen@gmail.com> References: <20230905151257.729192-1-hengqi.chen@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-0.7 required=5.0 tests=BAYES_00,DATE_IN_PAST_24_48, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS,T_FILL_THIS_FORM_SHORT autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: bpf@iogearbox.net In current implementation, we assume that symbol found in .dynsym section would have a version suffix and use it to compare with symbol user supplied. According to the spec ([0]), this assumption is incorrect, the version info of dynamic symbols are stored in .gnu.version and .gnu.version_d sections of ELF objects. For example: $ nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep rwlock_wrlock 000000000009b1a0 T __pthread_rwlock_wrlock@GLIBC_2.2.5 000000000009b1a0 T pthread_rwlock_wrlock@@GLIBC_2.34 000000000009b1a0 T pthread_rwlock_wrlock@GLIBC_2.2.5 $ readelf -W --dyn-syms /lib/x86_64-linux-gnu/libc.so.6 | grep rwlock_wrlock 706: 000000000009b1a0 878 FUNC GLOBAL DEFAULT 15 __pthread_rwlock_wrlock@GLIBC_2.2.5 2568: 000000000009b1a0 878 FUNC GLOBAL DEFAULT 15 pthread_rwlock_wrlock@@GLIBC_2.34 2571: 000000000009b1a0 878 FUNC GLOBAL DEFAULT 15 pthread_rwlock_wrlock@GLIBC_2.2.5 In this case, specify pthread_rwlock_wrlock@@GLIBC_2.34 or pthread_rwlock_wrlock@GLIBC_2.2.5 in bpf_uprobe_opts::func_name won't work. Because the qualified name does NOT match `pthread_rwlock_wrlock` (without version suffix) in .dynsym sections. This commit implements the symbol versioning for dynsym and allows user to specify symbol in the following forms: - func - func@LIB_VERSION - func@@LIB_VERSION In case of symbol conflicts, error out and users should resolve it by specifying a qualified name. [0]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html Signed-off-by: Hengqi Chen Reviewed-by: Alan Maguire Acked-by: Jiri Olsa --- tools/lib/bpf/elf.c | 145 +++++++++++++++++++++++++++++++++++++---- tools/lib/bpf/libbpf.c | 2 +- 2 files changed, 133 insertions(+), 14 deletions(-) -- 2.34.1 diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c index 5c9e588b17da..2d2819b7e018 100644 --- a/tools/lib/bpf/elf.c +++ b/tools/lib/bpf/elf.c @@ -1,5 +1,8 @@ // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif #include #include #include @@ -10,6 +13,17 @@ #define STRERR_BUFSIZE 128 +/* A SHT_GNU_versym section holds 16-bit words. This bit is set if + * the symbol is hidden and can only be seen when referenced using an + * explicit version number. This is a GNU extension. + */ +#define VERSYM_HIDDEN 0x8000 + +/* This is the mask for the rest of the data in a word read from a + * SHT_GNU_versym section. + */ +#define VERSYM_VERSION 0x7fff + int elf_open(const char *binary_path, struct elf_fd *elf_fd) { char errmsg[STRERR_BUFSIZE]; @@ -64,11 +78,14 @@ struct elf_sym { const char *name; GElf_Sym sym; GElf_Shdr sh; + int ver; + bool hidden; }; struct elf_sym_iter { Elf *elf; Elf_Data *syms; + Elf_Data *versyms; size_t nr_syms; size_t strtabidx; size_t next_sym_idx; @@ -111,6 +128,18 @@ static int elf_sym_iter_new(struct elf_sym_iter *iter, iter->nr_syms = iter->syms->d_size / sh.sh_entsize; iter->elf = elf; iter->st_type = st_type; + + /* Version symbol table is meaningful to dynsym only */ + if (sh_type != SHT_DYNSYM) + return 0; + + scn = elf_find_next_scn_by_type(elf, SHT_GNU_versym, NULL); + if (!scn) + return 0; + if (!gelf_getshdr(scn, &sh)) + return -EINVAL; + iter->versyms = elf_getdata(scn, 0); + return 0; } @@ -119,6 +148,7 @@ static struct elf_sym *elf_sym_iter_next(struct elf_sym_iter *iter) struct elf_sym *ret = &iter->sym; GElf_Sym *sym = &ret->sym; const char *name = NULL; + GElf_Versym versym; Elf_Scn *sym_scn; size_t idx; @@ -138,12 +168,112 @@ static struct elf_sym *elf_sym_iter_next(struct elf_sym_iter *iter) iter->next_sym_idx = idx + 1; ret->name = name; + ret->ver = 0; + ret->hidden = false; + + if (iter->versyms) { + if (!gelf_getversym(iter->versyms, idx, &versym)) + continue; + ret->ver = versym & VERSYM_VERSION; + ret->hidden = versym & VERSYM_HIDDEN; + } return ret; } return NULL; } +static const char *elf_get_vername(Elf *elf, int ver) +{ + GElf_Verdaux verdaux; + GElf_Verdef verdef; + Elf_Data *verdefs; + size_t strtabidx; + GElf_Shdr sh; + Elf_Scn *scn; + int offset; + + scn = elf_find_next_scn_by_type(elf, SHT_GNU_verdef, NULL); + if (!scn) + return NULL; + if (!gelf_getshdr(scn, &sh)) + return NULL; + strtabidx = sh.sh_link; + verdefs = elf_getdata(scn, 0); + + offset = 0; + while (gelf_getverdef(verdefs, offset, &verdef)) { + if (verdef.vd_ndx != ver) { + if (!verdef.vd_next) + break; + + offset += verdef.vd_next; + continue; + } + + if (!gelf_getverdaux(verdefs, offset + verdef.vd_aux, &verdaux)) + break; + + return elf_strptr(elf, strtabidx, verdaux.vda_name); + + } + return NULL; +} + +static bool symbol_match(Elf *elf, int sh_type, struct elf_sym *sym, const char *name) +{ + size_t name_len, sname_len; + bool is_name_qualified; + const char *ver; + char *sname; + int ret; + + name_len = strlen(name); + /* Does name specify "@LIB" or "@@LIB" ? */ + is_name_qualified = strstr(name, "@") != NULL; + + /* If user specify a qualified name, for dynamic symbol, + * it is in form of func, NOT func@LIB_VER or func@@LIB_VER. + * So construct a full quailified symbol name using versym info + * for comparison. + */ + if (is_name_qualified && sh_type == SHT_DYNSYM) { + /* Make sure func match func@LIB_VER */ + sname_len = strlen(sym->name); + if (strncmp(sym->name, name, sname_len) != 0) + return false; + + /* But not func2@LIB_VER */ + if (name[sname_len] != '@') + return false; + + ver = elf_get_vername(elf, sym->ver); + if (!ver) + return false; + + ret = asprintf(&sname, "%s%s%s", sym->name, + sym->hidden ? "@" : "@@", ver); + if (ret == -1) + return false; + + ret = strncmp(sname, name, name_len); + free(sname); + return ret == 0; + } + + /* Otherwise, for normal symbols or non-qualified names + * User can specify func, func@@LIB or func@@LIB_VERSION. + */ + if (strncmp(sym->name, name, name_len) != 0) + return false; + /* ...but we don't want a search for "foo" to match 'foo2" also, so any + * additional characters in sname should be of the form "@LIB" or "@@LIB". + */ + if (!is_name_qualified && sym->name[name_len] != '\0' && sym->name[name_len] != '@') + return false; + + return true; +} /* Transform symbol's virtual address (absolute for binaries and relative * for shared libs) into file offset, which is what kernel is expecting @@ -166,9 +296,8 @@ static unsigned long elf_sym_offset(struct elf_sym *sym) long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name) { int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB }; - bool is_shared_lib, is_name_qualified; + bool is_shared_lib; long ret = -ENOENT; - size_t name_len; GElf_Ehdr ehdr; if (!gelf_getehdr(elf, &ehdr)) { @@ -179,10 +308,6 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name) /* for shared lib case, we do not need to calculate relative offset */ is_shared_lib = ehdr.e_type == ET_DYN; - name_len = strlen(name); - /* Does name specify "@@LIB"? */ - is_name_qualified = strstr(name, "@@") != NULL; - /* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if * a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically * linked binary may not have SHT_DYMSYM, so absence of a section should not be @@ -201,13 +326,7 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name) goto out; while ((sym = elf_sym_iter_next(&iter))) { - /* User can specify func, func@@LIB or func@@LIB_VERSION. */ - if (strncmp(sym->name, name, name_len) != 0) - continue; - /* ...but we don't want a search for "foo" to match 'foo2" also, so any - * additional characters in sname should be of the form "@@LIB". - */ - if (!is_name_qualified && sym->name[name_len] != '\0' && sym->name[name_len] != '@') + if (!symbol_match(elf, sh_types[i], sym, name)) continue; cur_bind = GELF_ST_BIND(sym->sym.st_info); diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 96ff1aa4bf6a..30b8f96820a7 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -11512,7 +11512,7 @@ static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf *link = NULL; - n = sscanf(prog->sec_name, "%m[^/]/%m[^:]:%m[a-zA-Z0-9_.]+%li", + n = sscanf(prog->sec_name, "%m[^/]/%m[^:]:%m[a-zA-Z0-9_.@]+%li", &probe_type, &binary_path, &func_name, &offset); switch (n) { case 1: From patchwork Tue Sep 5 15:12:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hengqi Chen X-Patchwork-Id: 13376514 X-Patchwork-Delegate: bpf@iogearbox.net Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E24D829B4 for ; Thu, 7 Sep 2023 15:35:27 +0000 (UTC) Received: from mail-yw1-x112d.google.com (mail-yw1-x112d.google.com [IPv6:2607:f8b0:4864:20::112d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1F309171C for ; Thu, 7 Sep 2023 08:35:06 -0700 (PDT) Received: by mail-yw1-x112d.google.com with SMTP id 00721157ae682-5921a962adfso11312457b3.1 for ; Thu, 07 Sep 2023 08:35:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1694100893; x=1694705693; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=wuVD3KjxuDmtHrR4M83KRmqZpQz2LCH9fQVoJWYT3Ec=; b=GKGBlDa5W65q08TIofkkai3hv/OWfKfnDIUgLK3vwGh/tkQd5+K/DtB3LN6r+7RKAt zrkmNkk6xInTCvNrpPNWt5C8JlXMlxD4Ba0Zbx1SrZvFbqQsuKdenrlBinfW0rr/hlgO My0cpOWkfKqhwcxQ5wwmJ8p4qfo57ws2edYaES4pR9p/B+wnXZqfgvVZG61R4q0yfHus C8kvf7Yp8O43B8gFpDbLjHYBTnSXO9GFoFLbG73YeJ4iXjX06BEuy334Ix0zmWQ00Ni1 XZPBAwer2GTwLyMVQcbKf7H3sqe2+nc+2KjMC80Ttj82wiUQ+w2V0Qv2Scx+b9ot3fyl inDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1694100893; x=1694705693; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=wuVD3KjxuDmtHrR4M83KRmqZpQz2LCH9fQVoJWYT3Ec=; b=DPeUquAeWTTcNbJfjyTQRJc7MQEhJM6AMJwN0aOZjGLjzLwICaIn+qQ+6+/x3EsGAk kGnzrw6el5OPJsXI4pcr+crexjAMw9HKVEBdDdBfk8+hknANsSzq4NkRE1HHV7qka+gP u5Fl4u4JqAclLyKBnND7Hi/H5P/iXiKOCk8GXtIpM0rxUx2/Y7qOM9W/xN32zPEchbq2 bPwr53yhyCn0Ym/m2rpS4+pB1NFNc7piBQrCRELh/3EJSMEwwIwTUaxQdpH44agR8HWz D2VVaKZni29XXotEbKHAb/PdNamEe74rXz+4BA790U5e2/wVizgwDZbYbtLlGLh5ohsJ gVZQ== X-Gm-Message-State: AOJu0YwLpPoo7QDJfX8zHPyGyXwpJXShzlSyfcVSX1oqjwykR2XLbxqP d+5blLwmVoa2PTbN/hLSHWKX5EDwTBp/IA== X-Google-Smtp-Source: AGHT+IFJUJS5VLjZs0opS9/al5EA/ZPRMII8l0XDOJZBnoucUGmMhLSWfNDOnhvE4JCO1hCIgFEZ0g== X-Received: by 2002:a05:6a20:4a20:b0:14d:cca3:a100 with SMTP id fr32-20020a056a204a2000b0014dcca3a100mr13395590pzb.36.1694065431385; Wed, 06 Sep 2023 22:43:51 -0700 (PDT) Received: from ubuntu.. ([43.132.98.117]) by smtp.googlemail.com with ESMTPSA id q7-20020a637507000000b00570574feda0sm11860963pgc.19.2023.09.06.22.43.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Sep 2023 22:43:51 -0700 (PDT) From: Hengqi Chen To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, alan.maguire@oracle.com, olsajiri@gmail.com, hengqi.chen@gmail.com Subject: [PATCH bpf-next v2 3/3] selftests/bpf: Add tests for symbol versioning for uprobe Date: Tue, 5 Sep 2023 15:12:57 +0000 Message-Id: <20230905151257.729192-4-hengqi.chen@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230905151257.729192-1-hengqi.chen@gmail.com> References: <20230905151257.729192-1-hengqi.chen@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-0.8 required=5.0 tests=BAYES_00,DATE_IN_PAST_24_48, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: bpf@iogearbox.net This exercises the newly added dynsym symbol versioning logics. Now we accept symbols in form of func, func@LIB_VERSION or func@@LIB_VERSION. The test rely on liburandom_read.so. For liburandom_read.so, we have: $ nm -D liburandom_read.so w __cxa_finalize@GLIBC_2.17 w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000000000 A LIBURANDOM_READ_1.0.0 0000000000000000 A LIBURANDOM_READ_2.0.0 000000000000081c T urandlib_api@@LIBURANDOM_READ_2.0.0 0000000000000814 T urandlib_api@LIBURANDOM_READ_1.0.0 0000000000000824 T urandlib_api_sameoffset@LIBURANDOM_READ_1.0.0 0000000000000824 T urandlib_api_sameoffset@@LIBURANDOM_READ_2.0.0 000000000000082c T urandlib_read_without_sema@@LIBURANDOM_READ_1.0.0 00000000000007c4 T urandlib_read_with_sema@@LIBURANDOM_READ_1.0.0 0000000000011018 D urandlib_read_with_sema_semaphore@@LIBURANDOM_READ_1.0.0 For `urandlib_api`, specifying `urandlib_api` will cause a conflict because there are two symbols named urandlib_api and both are global bind. For `urandlib_api_sameoffset`, there are also two symbols in the .so, but both are at the same offset and essentially they refer to the same function so no conflict. Signed-off-by: Hengqi Chen Reviewed-by: Alan Maguire Acked-by: Jiri Olsa --- tools/testing/selftests/bpf/Makefile | 5 +- .../testing/selftests/bpf/liburandom_read.map | 15 +++ .../testing/selftests/bpf/prog_tests/uprobe.c | 95 +++++++++++++++++++ .../testing/selftests/bpf/progs/test_uprobe.c | 61 ++++++++++++ tools/testing/selftests/bpf/urandom_read.c | 9 ++ .../testing/selftests/bpf/urandom_read_lib1.c | 41 ++++++++ 6 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/bpf/liburandom_read.map create mode 100644 tools/testing/selftests/bpf/prog_tests/uprobe.c create mode 100644 tools/testing/selftests/bpf/progs/test_uprobe.c -- 2.34.1 diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index edef49fcd23e..49ff9c716b79 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -193,11 +193,12 @@ endif # Filter out -static for liburandom_read.so and its dependent targets so that static builds # do not fail. Static builds leave urandom_read relying on system-wide shared libraries. -$(OUTPUT)/liburandom_read.so: urandom_read_lib1.c urandom_read_lib2.c +$(OUTPUT)/liburandom_read.so: urandom_read_lib1.c urandom_read_lib2.c liburandom_read.map $(call msg,LIB,,$@) $(Q)$(CLANG) $(filter-out -static,$(CFLAGS) $(LDFLAGS)) \ - $^ $(filter-out -static,$(LDLIBS)) \ + $(filter %.c,$^) $(filter-out -static,$(LDLIBS)) \ -fuse-ld=$(LLD) -Wl,-znoseparate-code -Wl,--build-id=sha1 \ + -Wl,--version-script=liburandom_read.map \ -fPIC -shared -o $@ $(OUTPUT)/urandom_read: urandom_read.c urandom_read_aux.c $(OUTPUT)/liburandom_read.so diff --git a/tools/testing/selftests/bpf/liburandom_read.map b/tools/testing/selftests/bpf/liburandom_read.map new file mode 100644 index 000000000000..38a97a419a04 --- /dev/null +++ b/tools/testing/selftests/bpf/liburandom_read.map @@ -0,0 +1,15 @@ +LIBURANDOM_READ_1.0.0 { + global: + urandlib_api; + urandlib_api_sameoffset; + urandlib_read_without_sema; + urandlib_read_with_sema; + urandlib_read_with_sema_semaphore; + local: + *; +}; + +LIBURANDOM_READ_2.0.0 { + global: + urandlib_api; +} LIBURANDOM_READ_1.0.0; diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe.c b/tools/testing/selftests/bpf/prog_tests/uprobe.c new file mode 100644 index 000000000000..6c619bcca41c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/uprobe.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2021 Hengqi Chen */ + +#include +#include "test_uprobe.skel.h" + +static FILE *urand_spawn(int *pid) +{ + FILE *f; + + /* urandom_read's stdout is wired into f */ + f = popen("./urandom_read 1 report-pid", "r"); + if (!f) + return NULL; + + if (fscanf(f, "%d", pid) != 1) { + pclose(f); + errno = EINVAL; + return NULL; + } + + return f; +} + +static int urand_trigger(FILE **urand_pipe) +{ + int exit_code; + + /* pclose() waits for child process to exit and returns their exit code */ + exit_code = pclose(*urand_pipe); + *urand_pipe = NULL; + + return exit_code; +} + +void test_uprobe(void) +{ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); + struct test_uprobe *skel; + FILE *urand_pipe = NULL; + int urand_pid = 0, err; + + skel = test_uprobe__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + urand_pipe = urand_spawn(&urand_pid); + if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn")) + goto cleanup; + + skel->bss->my_pid = urand_pid; + + /* Manual attach uprobe to urandlib_api + * There are two `urandlib_api` symbols in .dynsym section: + * - urandlib_api@LIBURANDOM_READ_1.0.0 + * - urandlib_api@LIBURANDOM_READ_1.0.0 + * Both are global bind and would cause a conflict if user + * specify the symbol name without a version suffix + */ + uprobe_opts.func_name = "urandlib_api"; + skel->links.test4 = bpf_program__attach_uprobe_opts(skel->progs.test4, + urand_pid, + "./liburandom_read.so", + 0 /* offset */, + &uprobe_opts); + if (!ASSERT_ERR_PTR(skel->links.test4, "urandlib_api_attach_conflict")) + goto cleanup; + + uprobe_opts.func_name = "urandlib_api@LIBURANDOM_READ_1.0.0"; + skel->links.test4 = bpf_program__attach_uprobe_opts(skel->progs.test4, + urand_pid, + "./liburandom_read.so", + 0 /* offset */, + &uprobe_opts); + if (!ASSERT_OK_PTR(skel->links.test4, "urandlib_api_attach_ok")) + goto cleanup; + + /* Auto attach 3 uprobes to urandlib_api_sameoffset */ + err = test_uprobe__attach(skel); + if (!ASSERT_OK(err, "skel_attach")) + goto cleanup; + + /* trigger urandom_read */ + ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code"); + + ASSERT_EQ(skel->bss->test1_result, 1, "urandlib_api_sameoffset"); + ASSERT_EQ(skel->bss->test2_result, 1, "urandlib_api_sameoffset@v1"); + ASSERT_EQ(skel->bss->test3_result, 1, "urandlib_api_sameoffset@@v2"); + ASSERT_EQ(skel->bss->test4_result, 1, "urandlib_api"); + +cleanup: + if (urand_pipe) + pclose(urand_pipe); + test_uprobe__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/test_uprobe.c b/tools/testing/selftests/bpf/progs/test_uprobe.c new file mode 100644 index 000000000000..8680280558da --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_uprobe.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Hengqi Chen */ + +#include "vmlinux.h" +#include +#include + +pid_t my_pid = 0; + +int test1_result = 0; +int test2_result = 0; +int test3_result = 0; +int test4_result = 0; + +SEC("uprobe/./liburandom_read.so:urandlib_api_sameoffset") +int BPF_UPROBE(test1) +{ + pid_t pid = bpf_get_current_pid_tgid() >> 32; + + if (pid != my_pid) + return 0; + + test1_result = 1; + return 0; +} + +SEC("uprobe/./liburandom_read.so:urandlib_api_sameoffset@LIBURANDOM_READ_1.0.0") +int BPF_UPROBE(test2) +{ + pid_t pid = bpf_get_current_pid_tgid() >> 32; + + if (pid != my_pid) + return 0; + + test2_result = 1; + return 0; +} + +SEC("uprobe/./liburandom_read.so:urandlib_api_sameoffset@@LIBURANDOM_READ_2.0.0") +int BPF_UPROBE(test3) +{ + pid_t pid = bpf_get_current_pid_tgid() >> 32; + + if (pid != my_pid) + return 0; + + test3_result = 1; + return 0; +} + +SEC("uprobe") +int BPF_UPROBE(test4) +{ + pid_t pid = bpf_get_current_pid_tgid() >> 32; + + if (pid != my_pid) + return 0; + + test4_result = 1; + return 0; +} diff --git a/tools/testing/selftests/bpf/urandom_read.c b/tools/testing/selftests/bpf/urandom_read.c index e92644d0fa75..b28e910a8fbb 100644 --- a/tools/testing/selftests/bpf/urandom_read.c +++ b/tools/testing/selftests/bpf/urandom_read.c @@ -21,6 +21,11 @@ void urand_read_without_sema(int iter_num, int iter_cnt, int read_sz); void urandlib_read_with_sema(int iter_num, int iter_cnt, int read_sz); void urandlib_read_without_sema(int iter_num, int iter_cnt, int read_sz); +int urandlib_api(void); +__asm__(".symver urandlib_api_old,urandlib_api@LIBURANDOM_READ_1.0.0"); +int urandlib_api_old(void); +int urandlib_api_sameoffset(void); + unsigned short urand_read_with_sema_semaphore SEC(".probes"); static __attribute__((noinline)) @@ -83,6 +88,10 @@ int main(int argc, char *argv[]) urandom_read(fd, count); + urandlib_api(); + urandlib_api_old(); + urandlib_api_sameoffset(); + close(fd); return 0; } diff --git a/tools/testing/selftests/bpf/urandom_read_lib1.c b/tools/testing/selftests/bpf/urandom_read_lib1.c index 86186e24b740..403b0735e223 100644 --- a/tools/testing/selftests/bpf/urandom_read_lib1.c +++ b/tools/testing/selftests/bpf/urandom_read_lib1.c @@ -11,3 +11,44 @@ void urandlib_read_with_sema(int iter_num, int iter_cnt, int read_sz) { STAP_PROBE3(urandlib, read_with_sema, iter_num, iter_cnt, read_sz); } + +/* Symbol versioning is different between static and shared library. + * Properly versioned symbols are needed for shared library, but + * only the symbol of the new version is needed for static library. + * Starting with GNU C 10, use symver attribute instead of .symver assembler + * directive, which works better with GCC LTO builds. + */ +#if defined(__GNUC__) && __GNUC__ >= 10 + +#define DEFAULT_VERSION(internal_name, api_name, version) \ + __attribute__((symver(#api_name "@@" #version))) +#define COMPAT_VERSION(internal_name, api_name, version) \ + __attribute__((symver(#api_name "@" #version))) + +#else + +#define COMPAT_VERSION(internal_name, api_name, version) \ + asm(".symver " #internal_name "," #api_name "@" #version); +#define DEFAULT_VERSION(internal_name, api_name, version) \ + asm(".symver " #internal_name "," #api_name "@@" #version); + +#endif + +COMPAT_VERSION(urandlib_api_v1, urandlib_api, LIBURANDOM_READ_1.0.0) +int urandlib_api_v1(void) +{ + return 1; +} + +DEFAULT_VERSION(urandlib_api_v2, urandlib_api, LIBURANDOM_READ_2.0.0) +int urandlib_api_v2(void) +{ + return 2; +} + +COMPAT_VERSION(urandlib_api_sameoffset, urandlib_api_sameoffset, LIBURANDOM_READ_1.0.0) +DEFAULT_VERSION(urandlib_api_sameoffset, urandlib_api_sameoffset, LIBURANDOM_READ_2.0.0) +int urandlib_api_sameoffset(void) +{ + return 3; +}