简体   繁体   English

GCC中strlen()的实现在哪里?

[英]Where is the implementation of strlen() in GCC?

Can anyone point me to the definition of strlen() in GCC? 谁能指点我在GCC中定义strlen() I've been grepping release 4.4.2 for about a half hour now (while Googling like crazy) and I can't seem to find where strlen() is actually implemented. 我一直在试用版本4.4.2大约半小时(当谷歌疯狂时),我似乎无法找到strlen()实际实现的位置。

You should be looking in glibc, not GCC -- it seems to be defined in strlen.c -- here's a link to strlen.c for glibc version 2.7 ... And here is a link to the glibc SVN repository online for strlen.c . 您应该在glibc中查找,而不是GCC - 它似乎是在strlen.c定义的 - 这里是glibc版本2.7的strlen.c链接...这里是strlenglibc SVN存储库链接。 c

The reason you should be looking at glibc and not gcc is: 你应该看glibc而不是gcc的原因是:

The GNU C library is used as the C library in the GNU system and most systems with the Linux kernel. GNU C库用作GNU系统中 C库,大多数系统使用Linux内核。

I realize this question is 4yrs old, but gcc will often include its own copy of strlen if you do not #include <string.h> and none of the answers (including the accepted answer) account for that. 我意识到这个问题已经过了4年了,但是如果你没有#include <string.h> ,那么gcc通常会包含它自己的strlen副本,并且没有任何答案(包括接受的答案)。 If you forget, you will get a warning: 如果您忘了,您会收到警告:

file_name:line_number: warning: incompatible implicit declaration of built-in function 'strlen'

and gcc will inline its copy which on x86 is the repnz scasb asm variant unless you pass -Werror or -fno-builtin. 并且gcc将内联其副本,在x86上是repnz scasb asm变体,除非你传递-Werror或-fno-builtin。 The files related to this are in gcc/config/<platform>/<platform>.{c,md} 与此相关的文件位于gcc/config/<platform>/<platform>.{c,md}

It is also controlled by gcc/builtins.c. 它也由gcc / builtins.c控制。 In case you wondered if and how a strlen() was optimized to a constant, see the function defined as tree c_strlen(tree src, int only_value) in this file. 如果您想知道strlen()是否以及如何针对常量进行优化,请参阅此文件中定义为tree c_strlen(tree src, int only_value)的函数。 It also controls how strlen (amongst others) is expanded and folded (based on the previously mentioned config/platform) 它还控制strlen(以及其他)如何扩展和折叠(基于前面提到的配置/平台)

Here's the bsd implementation 这是bsd实现

size_t
strlen(const char *str)
{
        const char *s;

        for (s = str; *s; ++s)
                ;
        return (s - str);
}

defined in glibc/string/strlen.c glibc / string / strlen.c中定义

#include <string.h>
#include <stdlib.h>

#undef strlen

#ifndef STRLEN
# define STRLEN strlen
#endif

/* Return the length of the null-terminated string STR.  Scan for
   the null terminator quickly by testing four bytes at a time.  */
size_t
STRLEN (const char *str)
{
  const char *char_ptr;
  const unsigned long int *longword_ptr;
  unsigned long int longword, himagic, lomagic;

  /* Handle the first few characters by reading one character at a time.
     Do this until CHAR_PTR is aligned on a longword boundary.  */
  for (char_ptr = str; ((unsigned long int) char_ptr
            & (sizeof (longword) - 1)) != 0;
       ++char_ptr)
    if (*char_ptr == '\0')
      return char_ptr - str;

  /* All these elucidatory comments refer to 4-byte longwords,
     but the theory applies equally well to 8-byte longwords.  */

  longword_ptr = (unsigned long int *) char_ptr;

  /* Bits 31, 24, 16, and 8 of this number are zero.  Call these bits
     the "holes."  Note that there is a hole just to the left of
     each byte, with an extra at the end:

     bits:  01111110 11111110 11111110 11111111
     bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD

     The 1-bits make sure that carries propagate to the next 0-bit.
     The 0-bits provide holes for carries to fall into.  */
  himagic = 0x80808080L;
  lomagic = 0x01010101L;
  if (sizeof (longword) > 4)
    {
      /* 64-bit version of the magic.  */
      /* Do the shift in two steps to avoid a warning if long has 32 bits.  */
      himagic = ((himagic << 16) << 16) | himagic;
      lomagic = ((lomagic << 16) << 16) | lomagic;
    }
  if (sizeof (longword) > 8)
    abort ();

  /* Instead of the traditional loop which tests each character,
     we will test a longword at a time.  The tricky part is testing
     if *any of the four* bytes in the longword in question are zero.  */
  for (;;)
    {
      longword = *longword_ptr++;

      if (((longword - lomagic) & ~longword & himagic) != 0)
    {
      /* Which of the bytes was the zero?  If none of them were, it was
         a misfire; continue the search.  */

      const char *cp = (const char *) (longword_ptr - 1);

      if (cp[0] == 0)
        return cp - str;
      if (cp[1] == 0)
        return cp - str + 1;
      if (cp[2] == 0)
        return cp - str + 2;
      if (cp[3] == 0)
        return cp - str + 3;
      if (sizeof (longword) > 4)
        {
          if (cp[4] == 0)
        return cp - str + 4;
          if (cp[5] == 0)
        return cp - str + 5;
          if (cp[6] == 0)
        return cp - str + 6;
          if (cp[7] == 0)
        return cp - str + 7;
        }
    }
    }
}
libc_hidden_builtin_def (strlen)

Is this what you are looking for? 这是你想要的? strlen() source . strlen()来源 See the git repository for more information. 有关更多信息,请参阅git存储库 The glibc resources page has links to the git repositories if you want to grab them rather than looking at the web view. 如果你想抓住它们而不是查看网页视图,那么glibc资源页面会链接到git存储库。

Google Code Search is a good starting point for questions like that. 谷歌代码搜索是这类问题的一个很好的起点。 They usually point to various different sources and implementations of a function. 它们通常指向函数的各种不同来源和实现。

In your particular case: GoogleCodeSearch(strlen) 在您的特定情况下: GoogleCodeSearch(strlen)

Google Code Search was completely shut down on March 2013 Google Code Search于2013年3月完全关闭

glibc 2.26 has several hand optimized assembly implementations of strlen glibc 2.26有几个手动优化的strlen组件实现

As of glibc-2.26 , a quick: 截至glibc-2.26 ,快速:

git ls-files | grep strlen.S

in the glibc tree shows a dozen of assembly hand-optimized implementations for all major archs and variations. 在glibc树中显示了十几个针对所有主要拱和变体的装配手动优化实现。

In particular, x86_64 alone has 3 variations: 特别是,仅x86_64有3种变体:

sysdeps/x86_64/multiarch/strlen-avx2.S
sysdeps/x86_64/multiarch/strlen-sse2.S
sysdeps/x86_64/strlen.S

A quick and dirty way to determine which one is used, is to step debug a test program: 确定使用哪一种的快速而肮脏的方法是逐步调试测试程序:

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main(void) {
    size_t size = 0x80000000, i, result;
    char *s = malloc(size);
    for (i = 0; i < size; ++i)
        s[i] = 'a';
    s[size - 1] = '\0';
    result = strlen(s);
    assert(result == size - 1);
    return EXIT_SUCCESS;
}

compiled with: 编译:

gcc -ggdb3 -std=c99 -O0 a.c

Off the bat: 蝙蝠:

disass main

contains: 包含:

callq  0x555555554590 <strlen@plt>

so the libc version is being called. 所以正在调用libc版本。

After a few si instruction level steps into that, GDB reaches: 经过几个si指令级步骤后,GDB达到:

__strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:52                                         
52      ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.

which tells me that strlen-avx2.S was used. 这告诉我使用了strlen-avx2.S

Then, I further confirm with: 然后,我进一步确认:

disass __strlen_avx2

and compare the disassembly with the glibc source. 并将反汇编与glibc源进行比较。

It is not surprising that the AVX2 version was used, since I have an i7-7820HQ CPU with launch date Q1 2017 and AVX2 support, and AVX2 is the most advanced of the assembly implementations, with launch date Q2 2013, while SSE2 is much more ancient from 2004. 使用AVX2版本并不奇怪,因为我有一个i7-7820HQ CPU,发布日期为2017年第一季度和AVX2支持,而AVX2是最先进的装配实施,发布日期为2013年第二季度,而SSE2则更多从2004年开始。

This is where a great part of the hardcoreness of glibc comes from: it has a lot of arch optimized hand written assembly code. 这是glibc硬性的很大一部分来自:它有很多arch优化的手写汇编代码。

Tested in Ubuntu 17.10, gcc 7.2.0, glibc 2.26. 测试在Ubuntu 17.10,gcc 7.2.0,glibc 2.26。

-O3

TODO: with -O3 , gcc does not use glibc's strlen , it just generates inline assembly, which is mentioned at: https://stackoverflow.com/a/19885891/895245 TODO:使用-O3 ,gcc不使用glibc的strlen ,它只生成内联汇编,如下所述: https//stackoverflow.com/a/19885891/895245

Is it because it can optimize even better? 是因为它可以更好地优化吗? But its output does not contain AVX2 instructions, so I feel that this is not the case. 但它的输出不包含AVX2指令,所以我觉得情况并非如此。

https://www.gnu.org/software/gcc/projects/optimize.html mentions: https://www.gnu.org/software/gcc/projects/optimize.html提及:

Deficiencies of GCC's optimizer GCC优化器的缺陷

glibc has inline assembler versions of various string functions; glibc具有各种字符串函数的内联汇编程序版本; GCC has some, but not necessarily the same ones on the same architectures. GCC在同一架构上有一些但不一定相同。 Additional optab entries, like the ones for ffs and strlen, could be provided for several more functions including memset, strchr, strcpy and strrchr. 可以为包括memset,strchr,strcpy和strrchr在内的多个函数提供其他optab条目,例如ffs和strlen的条目。

My simple tests show that the -O3 version is actually faster, so GCC made the right choice. 我的简单测试表明-O3版本实际上更快,因此GCC做出了正确的选择。

Asked at: https://www.quora.com/unanswered/How-does-GCC-know-that-its-builtin-implementation-of-strlen-is-faster-than-glibcs-when-using-optimization-level-O3 提问者: https//www.quora.com/unanswered/How-does-GCC-know-that-its-builtin-implementation-of-strlen-is-faster-than-glibcs​​-when-using-optimization-level -O3

Although the original poster may not have known this or been looking for this, gcc internally inlines a number of so-called "builtin" c functions that it defines on its own, including some of the mem*() functions and (depending on the gcc version) strlen. 虽然原始海报可能不知道这个或者一直在寻找这个,但gcc内部内联了一些所谓的“内置”c函数,它自己定义,包括一些mem *()函数和(取决于gcc版)strlen。 In such cases, the library version is essentially never used, and pointing the person at the version in glibc is not strictly speaking correct. 在这种情况下,库版本基本上从不使用,并且将该人指向glibc中的版本并不严格地说是正确的。 (It does this for performance reasons -- in addition to the improvement that inlining itself produces, gcc "knows" certain things about the functions when it provides them, such as, for example, that strlen is a pure function and that it can thus optimize away multiple calls, or in the case of the mem*() functions that no aliasing is taking place.) (它出于性能原因这样做 - 除了内联本身产生的改进之外,gcc在提供函数时“知道”关于函数的某些事情,例如,strlen是一个纯函数,因此它可以因此优化多个调用,或者在mem *()函数中没有发生混叠。)

For more information on this, see http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html 有关这方面的更多信息,请参阅http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

I realize that this is old question, you can find the linux kernel sources at github here , and the 32 bit implementation for strlen() could be found in strlen_32.c on github. 我意识到这是一个老问题,你可以在这里找到github上的linux内核源代码,strlen()的32位实现可以在github上的strlen_32.c中找到。 The mentioned file has this implementation. 提到的文件具有此实现。

#include <linux/types.h>
#include <linux/string.h>
#include <linux/module.h>

size_t strlen(const char *s)
{
    /* Get an aligned pointer. */
    const uintptr_t s_int = (uintptr_t) s;
    const uint32_t *p = (const uint32_t *)(s_int & -4);

    /* Read the first word, but force bytes before the string to be nonzero.
     * This expression works because we know shift counts are taken mod 32.
     */
    uint32_t v = *p | ((1 << (s_int << 3)) - 1);

    uint32_t bits;
    while ((bits = __insn_seqb(v, 0)) == 0)
        v = *++p;

    return ((const char *)p) + (__insn_ctz(bits) >> 3) - s;
}
EXPORT_SYMBOL(strlen);

You can use this code, the simpler the better ! 你可以使用这个代码,越简单就越好!

size_t Strlen ( const char * _str )
{
    size_t i = 0;
    while(_str[i++]);
    return i;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM