社区导航

 
快捷导航
  • 首页
  • 论坛
  • 查看新帖
  • 最新回复
  • 社区活动
  • 联系管理员
  • 消灭零回复
  • E金币兑换
  • 干货
搜索
查看: 638|回复: 6

[源码分析] 【glibC阅读】之 strlen实现

[复制链接]

7674

TA的帖子

54

TA的资源

版主

Rank: 6Rank: 6

发表于 2018-1-8 00:18:38 | 显示全部楼层 |阅读模式
本帖最后由 辛昕 于 2018-1-8 00:43 编辑

写在前面的话:
        其实不管是 strlen的实现,甚至是glibc的实现,对我来说其实都不是什么有多大实际价值的事情。我只是意识到,如果我真的要学会阅读代码,我必须真的去尝试阅读代码。
        而选择glibc,是因为它是一个通用的标准库,而且与我常年使用C有密切关系。
        这个帖子的内容,对于那些早已经熟悉这些套路的人(x86汇编和gcc大神)来说,简直小儿科到不足一提。
        不过,我相信对于大多数和我这样,对此几乎一窍不通的小白来说,这个过程本身是很有参考价值的。而我写下这个过程,本身也是为我逐渐积累阅读较大型代码库的能力和经验做准备。希望没有破坏什么人的雅兴和胃口。    

        此前发的帖子,那个strlen的glibc实现分析,太监了很久。周末用了所剩不多的零碎时间,尝试阅读一下。
当然现在也还是不十分明白,但是,先做个记录。

        环境说明:
        1.我用的glibC 源码版本是 2.14
        2.我用的souce insight阅读——否则我接下来叙述的内容,有可能出现部分名词不一致,可能影响理解。

        阅读方式:
        由于我仍然未能理解glibc的项目源码结构,所以我尚不能跑起任何一个小程序exe,我仅仅从代码的依赖关系上去分析。
我的分析起点正是   对 strlen() 邮件 go to definition......
此帖出自编程基础论坛
没有一件事情是容易的,所以,起念头时,一定要好好琢磨


回复

使用道具 举报

7674

TA的帖子

54

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-1-8 00:24:27 | 显示全部楼层
找下来,总共有四处定义——我不得不说,对于第一次阅读比较大的库的我来说,这是个下马威。

硬着头皮看,其中两处为 .c源文件,两处为.h头文件,以至于我以为两个是宏定义,但后来看并非如此,而是 内联函数。

内联函数在cpp里我见得比较多,但在C里实际上见的不多,因为很多时候,IAR也好,MDK也好,都无法使用。

闲话少说,以下四楼,分别列出。
没有一件事情是容易的,所以,起念头时,一定要好好琢磨


回复

使用道具 举报

7674

TA的帖子

54

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-1-8 00:25:11 | 显示全部楼层
本帖最后由 辛昕 于 2018-1-8 00:26 编辑

// 我根据自己喜欢的代码格式,做了一些很细微的调整,但未动一个字母。

  1. // 1 strlen - Function in Strlen.c (sysdeps\i386) at line 23 (13 lines)
  2. #include <string.h>

  3. size_t strlen (const char *str)
  4. {
  5.   int cnt;

  6.   asm(       "cld\n"                        /* Search forward.  */
  7.                                         /* Some old versions of gas need `repne' instead of `repnz'.  */
  8.       "repnz\n"                     /* Look for a zero byte.  */
  9.       "scasb" /* %0, %1, %3 */ :
  10.       "=c" (cnt) : "D" (str), "0" (-1), "a" (0));

  11.        return -2 - cnt;
  12. }
  13. libc_hidden_builtin_def (strlen)

  14. // 这段代码,显然是针对x86的实现,一堆asm,鬼看得懂,也不在意,在意的是 最后一句,是什么意思
  15. // 同时好奇的是,那其他几个实现是怎么回事
复制代码

没有一件事情是容易的,所以,起念头时,一定要好好琢磨


回复

使用道具 举报

7674

TA的帖子

54

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-1-8 00:27:30 | 显示全部楼层
本帖最后由 辛昕 于 2018-1-8 00:31 编辑
  1. //        *这个文件位于 根目录下 string/strlen.c
  2. //2 strlen - Function in Strlen.c (string) at line 29 (78 lines)
  3. #include <string.h>
  4. #include <stdlib.h>
  5.         
  6. #undef strlen                                        // 标注1

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

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

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

  23.   longword_ptr = (unsigned long int *) char_ptr;

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

  27.      bits:  01111110 11111110 11111110 11111111
  28.      bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD

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

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

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

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

  53.           if (cp[0] == 0)
  54.             return cp - str;
  55.           if (cp[1] == 0)
  56.             return cp - str + 1;
  57.           if (cp[2] == 0)
  58.             return cp - str + 2;
  59.           if (cp[3] == 0)
  60.             return cp - str + 3;
  61.           if (sizeof (longword) > 4)
  62.             {
  63.               if (cp[4] == 0)
  64.                 return cp - str + 4;
  65.               if (cp[5] == 0)
  66.                 return cp - str + 5;
  67.               if (cp[6] == 0)
  68.                 return cp - str + 6;
  69.               if (cp[7] == 0)
  70.                 return cp - str + 7;
  71.             }
  72.         }
  73.     }
  74. }
  75. libc_hidden_builtin_def (strlen)

  76. // 这是一段看起来完全与平台无关的代码。似乎可以认为是我们追踪的终点。
  77. // 我唯一有点好奇,相信也是你们好奇的,为什么,我们可以用区区几行代码实现的 strlen() 在这里居然这么复杂!
  78. // 另外,见标注1:这是在干什么?
  79. // 它要取消前面的(宏)定义 strlen,所以几乎可以料想,还有两个 strlen 的 宏定义(不要被小写迷惑了?)

复制代码

没有一件事情是容易的,所以,起念头时,一定要好好琢磨


回复

使用道具 举报

7674

TA的帖子

54

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-1-8 00:30:28 | 显示全部楼层
本帖最后由 辛昕 于 2018-1-8 00:35 编辑

//3 strlen - Macro in String.h (sysdeps\s390\bits) at line 43
        // 这段预警告似乎在暗示我们,这个定义是一个替代定义。
  1. #ifndef _STRING_H
  2. # error "Never use <bits/string.h> directly; include <string.h> instead."
  3. #endif
复制代码


// 紧随其后的是一个 strlen 的实现版本
  1. #ifndef _FORCE_INLINES
  2. #define strlen(str) __strlen_g ((str))

  3. __STRING_INLINE size_t __strlen_g (__const char *) __asm__ ("strlen");
  4. // 这段代码的解释:
  5. // __STRING_INLINE 这个没什么,搜索可以看到,它可能有三种定义:1.啥都没;2.inline;3.extern inline
  6. // 虽然我并不理解C下的 内联函数是怎么回事,但内联就只是内联而已,没什么特别的。
  7. // 至于 extern inline 我的确并不确切理解这个 外部是个什么鬼,但不管如何,也就只是个内联,在
  8. // 这次的阅读里,我真心丝毫不关心。
复制代码

  1. // __asm__ 倒是一个知识点,不过,也没什么特别,它是gcc的一个关键字,意思是接下来要使用汇编代码了。
  2. // 我还是不明白的是, 后面跟着一个 ("strlen")  这是个什么操作?

  3. // 百度到一个这样的东西,让我领悟到 这个 内嵌 asm 的语法
  4. /*
  5.         __asm__("mov r0, #0\n"}
  6. */

  7. // 所以上述的 这句话,其实大概率翻译成常见的形式就会是

  8. /*
  9.         inline size_t __strlen_g (__const char *)
  10.         {
  11.                 __asm__ ("strlen");        
  12.         }
  13.         
  14.         我并不明白这个地方为毛这么该死非要挤在一行上看,但显然,至少对于我,这样的格式我更容易看懂
  15.         我可以理解在汇编里出现 mov 之类的语句,
  16.         但出现一个 strlen 我是理解不了的。
  17. */
复制代码
  1. __STRING_INLINE size_t __strlen_g (__const char *__str)
  2. {
  3.     char *__ptr, *__tmp;

  4.     __ptr = (char *) 0;
  5.     __tmp = (char *) __str;
  6.     __asm__ __volatile__ ("   la    0,0\n"
  7.                           "0: srst  %0,%1\n"
  8.                           "   jo    0b\n"
  9.                           : "+&a" (__ptr), "+&a" (__tmp) :
  10.                           : "cc", "memory", "0" );
  11.     return (size_t) (__ptr - __str);
  12. }

  13. // 这一段对我来说倒没什么太特别的,无非就是一堆x86汇编代码,我看不懂,但反正就是在干strlen该干的事的意思
  14. #endif
复制代码

没有一件事情是容易的,所以,起念头时,一定要好好琢磨


回复

使用道具 举报

7674

TA的帖子

54

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-1-8 00:51:38 | 显示全部楼层
  1. // 先看最后一处 strlen 宏定义
  2. //4 strlen - Macro in String.h (sysdeps\i386\i486\bits) at line 549 (4 lines)
  3. /* Return the length of S.  */
  4. #define _HAVE_STRING_ARCH_strlen 1
  5. #define strlen(str) \
  6.   (__extension__ (__builtin_constant_p (str)                                      \
  7.                   ? __builtin_strlen (str)                                      \
  8.                   : __strlen_g (str)))
  9.                   
  10.                         // 标注2
  11. __STRING_INLINE size_t __strlen_g (__const char *__str);

  12. __STRING_INLINE size_t
  13. __strlen_g (__const char *__str)
  14. {
  15.   register char __dummy;
  16.   register __const char *__tmp = __str;
  17.   __asm__ __volatile__
  18.     ("1:\n\t"
  19.      "movb        (%0),%b1\n\t"
  20.      "leal        1(%0),%0\n\t"
  21.      "testb        %b1,%b1\n\t"
  22.      "jne        1b"
  23.      : "=r" (__tmp), "=&q" (__dummy)
  24.      : "0" (__str),
  25.        "m" ( *(struct { char __x[0xfffffff]; } *)__str)
  26.      : "cc" );
  27.   return __tmp - __str - 1;
  28. }

  29. /*
  30.         总体而言,这是一个和 第三处,其实十分相似的结构。
  31.         唯一要理解的是一个新的语法团——标注2
  32.        
  33. */
复制代码
没有一件事情是容易的,所以,起念头时,一定要好好琢磨


回复

使用道具 举报

7674

TA的帖子

54

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-1-8 00:52:11 | 显示全部楼层
我最终意识到,要真的继续玩下去。
除了去挖那些很可能是 x86汇编语法 或者是 gcc 语法。

否则,真的不知道这一个小小的strlen都能给我闹出4个定义,我鬼知道你最后到底用的是哪个定义啊?
彼此又是什么关系,又为毛要搞得这么复杂?

这些都是接下去要尝试做的事情。
没有一件事情是容易的,所以,起念头时,一定要好好琢磨


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

  • 论坛活动 E手掌握

    扫码关注
    EEWORLD 官方微信

  • EE福利  唾手可得

    扫码关注
    EE福利 唾手可得

小黑屋|手机版|Archiver|电子工程世界 ( 京ICP证 060456

GMT+8, 2018-6-21 00:54 , Processed in 0.194695 second(s), 14 queries , Gzip On, Redis On.

快速回复 返回顶部 返回列表