[20260919]oracle skgmhash函数的等价C以及python代码(整理版本).txt

[20260919]oracle skgmhash函数的等价C以及python代码(整理版本).txt

--//gdb反汇编oracle的skgmhash函数,生成汇编代码,提交给kimi,本来想问它采用什么hash算法,顺便要求生成c或者python代码,主
--//要想看看自己是否能读懂。经过反复沟通已经尝试,kimi终于生成正确的c代码。

--//我觉得提问时除了给出oracle的skgmhash函数汇编代码外,给出一些测试案例,也许可以大大节省时间,在给出的代码计算错误后,
--//我提供一些字符串的测试例子,还是错误,再给出一个字符a参与运算的例子,生成代码还是错误的,最后给出abc的字符串的测试例
--//子,生成才是正确的代码。

--//关于什么hash算法,kimi结论:
结论:不是公开标准算法,不是 MD5/SHA/CRC32/FNV/Murmur/xxHash;它是 Oracle 内部 skgmhash 的私有 32 位非加密字符串散列。按
行为归类,最接近:

    bit-wise polynomial / affine multiply-mix hash
    也就是"逐 bit 的 Horner/滚动散列 + 大奇数乘子扩散 + 高位折叠"。

核心递推可写成:
c

#define SKGM_D 0x3f449d13u   /* 1061461267 = 2^32 - 0xc0bb62ed */
...

for (size_t i = 0; i < len; i++) {
    uint8_t b = s[len - 1 - i];          /* 从最后一个字节开始 */
    for (int bit = 0; bit < 8; bit++) {  /* 字节内 LSB -> MSB */
        h = (h << 1) + ((b >> bit) & 1u);
        if (h >= SKGM_D)                 /* h 原小于 D,故 2h+bit < 2D,一次条件减即可 */
            h -= SKGM_D;
    }
}

其中:

    0x40bd8ded 是 32 位奇乘子,用来打散 bit 相关性;
    0xc0bb62ed ≡ -0x3f449d13 mod 2^32,用于高位折叠/近似取模;
    最终 return (h << 2) ?: 4,很像把 hash 当成 4 字节 bucket/entry 偏移用,且把 0 映射成 4 以避免空桶/无效值;
    只去掉一个尾部 /,长度 =0 或去 / 后为空返回 0。

--//注:如果仔细看下面生成的C代码,并没有出现0x40bd8ded变量.仔细看可以从0xc0bb62ed导出,第2,4字节是bd,ed相同,其他第1
--//,3字节,上下相加:
0x8d+0x62 = 0xef
0x40+0xc0 = 0x100
--//出现0xef视乎与移位相关,为什么使用这2个数字,具体细节超出我的能力,放弃探究。

--//建立c程序,代码如下:
$ cat skgmhash.c
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

/* skgmhash: 按位滚动 hash,每步 h = (2h + bit) mod D
 * 字节从尾到头、字节内位从 LSB 到 MSB(与反汇编的 rsi++/rcx-- 循环一致) */
#define SKGM_D 0x3f449d13u   /* 1061461267 = 2^32 - 0xc0bb62ed */

static uint32_t skgmhash(const unsigned char *s, size_t len)
{
    //if (len <= 1)
    //    return 0;

    if (s[len - 1] == '/')
        --len;

    if (len == 0)
        return 4;

    uint32_t h = 0;

    for (size_t i = 0; i < len; i++) {
        uint8_t b = s[len - 1 - i];          /* 从最后一个字节开始 */
        for (int bit = 0; bit < 8; bit++) {  /* 字节内 LSB -> MSB */
            h = (h << 1) + ((b >> bit) & 1u);
            if (h >= SKGM_D)                 /* h 原小于 D,故 2h+bit < 2D,一次条件减即可 */
                h -= SKGM_D;
        }
    }

    uint32_t r = h << 2;
    return r ? r : 4;
}

int main(int argc, char *argv[])
{
    const char *oracle_home_sid = "/u01/app/oracle/product/21.0.0/dbhome_1book";

    if (argc >= 2)
        oracle_home_sid = argv[1];

    uint32_t key = skgmhash((const unsigned char *)oracle_home_sid,
                            strlen(oracle_home_sid));
    printf("ORACLE_HOME_SID : %s\n", oracle_home_sid);
    printf("Key : 0x%x (%u)\n", key, key);
    return 0;
}

$ gcc skgmhash.c -o skgmhash

--//简单说明:
1.最后1个字符是/,长度减1. 这个应该从11.2.0.4开始支持。
2.每次取1个字符,从最后一个字节开始按bit运算,细节自己看。
3.最后的结果向左移动2位,如果是0返回4。理论讲算出来最后1个字符按16进制表示一定是0,4,8,C.
4.实际上最后生成的代码还是有点小错误,就是字符串长度小于等于1时,不是返回0,我注解了这段代码。直接修改了长度等于0,返回4.
--//注:其实我给出a字符串结果,为什么kimi还是犯这类低级错误。

$ ./skgmhash
ORACLE_HOME_SID : /u01/app/oracle/product/21.0.0/dbhome_1book
Key : 0xafa94c20 (2947107872)

 $ ./skgmhash /u01/app/oracle/product/21.0.0/dbhome_1book
ORACLE_HOME_SID : /u01/app/oracle/product/21.0.0/dbhome_1book
Key : 0xafa94c20 (2947107872)

$ ipcs -m
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 0          oracle     600        10485760   55
0x00000000 1          oracle     600        1140850688 55
0x00000000 2          oracle     600        8388608    55
0xafa94c20 3          oracle     600        2097152    55
--//与实际情况完全一致。

$ ./skgmhash /u01/app/oracle/product/21.0.0/dbhome_1book/
ORACLE_HOME_SID : /u01/app/oracle/product/21.0.0/dbhome_1book/
Key : 0xafa94c20 (2947107872)
--//结尾/不参与运算。

$ ./skgmhash //u01/app/oracle/product/21.0.0/dbhome_1book/
ORACLE_HOME_SID : //u01/app/oracle/product/21.0.0/dbhome_1book/
Key : 0xaf89bb44 (2945039172)
--//开头加/,计算结果不同。

$ ./skgmhash abc
ORACLE_HOME_SID : abc
Key : 0x3191a18 (51976728)

$ ./skgmhash a
ORACLE_HOME_SID : a
Key : 0x218 (536)

$ ./skgmhash /
ORACLE_HOME_SID : /
Key : 0x4 (4)

--//11g下的数据库测试,也能与实际的ipcs -m输出的key一致。
$ ./skgmhash /u01/app/oracle/product/11.2.0.4/db_1book
ORACLE_HOME_SID : /u01/app/oracle/product/11.2.0.4/db_1book
Key : 0x56c108b0 (1455491248)

$ ./skgmhash //u01/app/oracle/product/11.2.0.4/db_1book
ORACLE_HOME_SID : //u01/app/oracle/product/11.2.0.4/db_1book
Key : 0xbfc32dfc (3217239548)

--//测试Python代码:
 $ cat skgmhash.py
def skgmhash(data: bytes) -> int:
    """
    Oracle skgmhash 还原版
    - 字节从尾到头、字节内位 LSB -> MSB
    - 每处理一位: h = (2h + bit) mod 0x3f449d13
    - 返回 (h << 2),为 0 时返回 4
    if n <= 1:
       return 0
    """
    n = len(data)
    if data[-1] == 0x2f:          # 结尾 '/' 裁剪
        n -= 1
    if n == 0:
        return 4

    D = 0x3f449d13                # 1061461267
    h = 0
    for i in range(n - 1, -1, -1):    # 从最后一个字节开始
        b = data[i]
        for bit in range(8):          # 字节内 LSB -> MSB
            h = (h << 1) + ((b >> bit) & 1)
            if h >= D:                # h < D,故 2h+bit < 2D,一次条件减即可
                h -= D

    r = (h << 2) & 0xffffffff
    return r if r else 4

if __name__ == "__main__":
    tests = [
        (b"abc", 0x3191a18),
        (b"/u01/app/oracle/product/21.0.0/dbhome_1book", 0xafa94c20),
        (b"/u01/app/oracle/product/21.0.0/dbhome_1book.pga_#$", 0xa93a2388),
        (b"/u01/app/oracle/product/21.0.0/dbhome_1book/", 0xafa94c20),  # 尾斜杠裁剪
    ]
    for s, expect in tests:
        got = skgmhash(s)
        print(f"{hex(got):>12}  expect {hex(expect)}  {'OK' if got == expect else 'FAIL'}  {s.decode()}")

$ python3 skgmhash.py
   0x3191a18  expect 0x3191a18  OK  abc
  0xafa94c20  expect 0xafa94c20  OK  /u01/app/oracle/product/21.0.0/dbhome_1book
  0xa93a2388  expect 0xa93a2388  OK  /u01/app/oracle/product/21.0.0/dbhome_1book.pga_#$
  0xafa94c20  expect 0xafa94c20  OK  /u01/app/oracle/product/21.0.0/dbhome_1book/

--//python 与 c 代码结果一样,可以使用gdb调用skgmhash函数验证。

(gdb) p /x (unsigned int) skgmhash((char *)"abc", 3)
$2 = 0x3191a18

(gdb) p /x ((unsigned int (*)(char *, unsigned long))0x57909b0)((char *)"/u01/app/oracle/product/21.0.0/dbhome_1book", 43)
$5 = 0xafa94c20

(gdb) p /x ((unsigned int (*)(char *, unsigned long))skgmhash)((char *)"/u01/app/oracle/product/21.0.0/dbhome_1book", 43)
$6 = 0xafa94c20

(gdb) p /x (unsigned int) skgmhash((char *)"a", 1)
$7 = 0x218

(gdb) p /x (unsigned int) skgmhash((char *)"/", 1)
$2 = 0x3d0
(gdb) p /x (unsigned int) skgmhash((char *)"//", 2)
$3 = 0x3d0
--//实际上单个/字符还是参与运算的。

4.顺便贴上bpftrace的监测代码:
# cat skgmhash.bt
uprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:skgmhash
/ $1 == 0 || pid == $1 /
{
        printf("[%s] %s pid=%d ptr0=%016lx hash_text=%s length=%d\n", strftime("%H:%M:%S", nsecs), func,pid,arg0, buf(arg0,arg1),arg1);
}

uretprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:skgmhash
/ $1 == 0 || pid == $1 /
{
    printf("[%s] %s pid=%d return hash_value=%016lx %lu\n", strftime("%H:%M:%S", nsecs), func,pid, retval, retval);
}

# bpftrace skgmhash.bt 0
Attached 2 probes
[11:17:46] skgmhash pid=3775 ptr0=00007ffc6dd90130 hash_text=/u01/app/oracle/product/21.0.0/dbhome_1book length=43
[11:17:46] skgmhash pid=3775 return hash_value=00000000afa94c20 2947107872
[11:17:46] skgmhash pid=3775 ptr0=00007ffc6dd8e330 hash_text=/u01/app/oracle/product/21.0.0/dbhome_1book.pga_#$ length=50
[11:17:46] skgmhash pid=3775 return hash_value=00000000a93a2388 2839159688

--//gdb直接调用skgmhash。
(gdb) p /x (unsigned int) skgmhash((char *)"abc", 3)
$1 = 0x3191a18
(gdb) p /x (unsigned int) skgmhash((char *)"", 0)
$2 = 0x4

[11:19:55] skgmhash pid=3775 ptr0=000000001bf24d60 hash_text=abc length=3
[11:19:55] skgmhash pid=3775 return hash_value=0000000003191a18 51976728
[11:20:06] skgmhash pid=3775 ptr0=000000001bf24d80 hash_text= length=0
[11:20:06] skgmhash pid=3775 return hash_value=0000000000000004 4

4.最后修改C代码如下:

$ cat skgmhash.c
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

/* skgmhash: 按位滚动 hash,每步 h = (2h + bit) mod D
 * 字节从尾到头、字节内位从 LSB 到 MSB(与反汇编的 rsi++/rcx-- 循环一致) */
#define SKGM_D 0x3f449d13u   /* 1061461267 = 2^32 - 0xc0bb62ed */

static uint32_t skgmhash(const unsigned char *s, size_t len)
{
    //if (len <= 1)
    //    return 0;

    if (s[len - 1] == '/' && len>1 )
        --len;

    if (len == 0)
        return 4;

    uint32_t h = 0;

    for (size_t i = 0; i < len; i++) {
        uint8_t b = s[len - 1 - i];          /* 从最后一个字节开始 */
        for (int bit = 0; bit < 8; bit++) {  /* 字节内 LSB -> MSB */
            h = (h << 1) + ((b >> bit) & 1u);
            if (h >= SKGM_D)                 /* h 原小于 D,故 2h+bit < 2D,一次条件减即可 */
                h -= SKGM_D;
        }
    }

    uint32_t r = h << 2;
    return r ? r : 4;
}

int main(int argc, char *argv[])
{
    const char *oracle_home_sid = "/u01/app/oracle/product/21.0.0/dbhome_1book";

    if (argc >= 2)
        oracle_home_sid = argv[1];

    uint32_t key = skgmhash((const unsigned char *)oracle_home_sid,
                            strlen(oracle_home_sid));
    printf("ORACLE_HOME_SID : %s\n", oracle_home_sid);
    printf("Key : 0x%x (%u)\n", key, key);
    return 0;
}

$ gcc skgmhash.c -o skgmhash

$ ./skgmhash /
ORACLE_HOME_SID : /
Key : 0x3d0 (976)

$ ./skgmhash /u01/app/oracle/product/21.0.0/dbhome_1book/
ORACLE_HOME_SID : /u01/app/oracle/product/21.0.0/dbhome_1book/
Key : 0xafa94c20 (2947107872)

posted @ 2026-09-21 21:26  lfree  阅读(6)  评论(0)    收藏  举报