得到系统调用的函数签名

由于有时候需要获取一大堆系统调用的函数签名(glibc的),一个个查实在很麻烦,因此需要写一个程序来做,思路还是蛮简单的,就是从0开始循环,使用ausyscall获取每个系统调用的名称,然后再使用man 2来获取每个系统调用的函数签名,在这里最麻烦的就是要分析man 2的输出结果,因为一个函数可能是这样的(函数包含多行):

SYNOPSIS
       #include <sys/mman.h>

       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
       int munmap(void *addr, size_t length);

       See NOTES for information on feature test macro requirements.

也可能是这样的(函数所在行包含注释):

SYNOPSIS
       #include <unistd.h>

       int setpgid(pid_t pid, pid_t pgid);
       pid_t getpgid(pid_t pid);

       pid_t getpgrp(void);                 /* POSIX.1 version */
       pid_t getpgrp(pid_t pid);            /* BSD version */

       int setpgrp(void);                   /* System V version */
       int setpgrp(pid_t pid, pid_t pgid);  /* BSD version */

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       getpgid():
           _XOPEN_SOURCE >= 500
               || /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L

还有可能是这样的:

SYNOPSIS
       #include <signal.h>

       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       sigaction(): _POSIX_C_SOURCE

       siginfo_t: _POSIX_C_SOURCE >= 199309L

甚至还有名称不一致的,比如man 2 exit的结果里并没有exit,而是_exit

SYNOPSIS
       #include <unistd.h>

       void _exit(int status);

       #include <stdlib.h>

       void _Exit(int status);

所以虽然开始写了一个shell脚本来做这种事,for ((i=0; i&lt;333; i++)) do syscname=ausyscall ${i}; syscsig=man 2 ${syscname}|grep -m 1 "${syscname}(" ; echo &quot;${i} ${syscname} ${syscsig}&quot;; done;,但后来也只能用python程序了,如下:

#!/usr/bin/env python

import subprocess
import re

def strip_comment(line):
    line = line.strip()
    if line.endswith('*/') and line.startswith('/*'):
        return ''

    if line.endswith('*/') and line.find('/*') > 0:
        return line[:line.find('/*')].strip()

    if line.startswith('/*') and line.find('*/') > 0:
        return line[line.find('*/')+2:].strip()

    return line

def get_synopsis(manpage):
    lines = [_.strip() for _ in manpage.split('\n')]
    in_synopsis = False
    synopsis = []
    for line in lines:
        if in_synopsis:
            if line == 'DESCRIPTION':
                break
            else:
                synopsis.append(strip_comment(line))
        else:
            if line == 'SYNOPSIS':
                in_synopsis = True

    return synopsis

def get_functions(synopsis):
    in_function = False
    result = []
    for l in synopsis:
        m = re.match(r'^[a-z][a-z_0-9]+ [\*]?([a-z][a-z_0-9]+)[ ]?\(.*', l)
        if m is None:
            m = re.match(r'^unsigned int (alarm)\(.*', l)

        # one line
        if not in_function and m is not None and (l.count('(') == l.count(')') and l.endswith(');')):
            result.append(m.group(1) + " " + l)
            continue

        # first line
        if not in_function and m is not None and (l.count('(') == l.count(')')+1 and (l.endswith(',') or l.endswith('...'))): 
            result.append(m.group(1) + " " + l)
            in_function = True
            continue

        # middle line
        if in_function:
            result[-1] = result[-1] + l

        # last line
        if in_function and (l.count('(')+1 == l.count(')') and l.endswith(');')):
            in_function = False
            continue

    return result

def get_apropos(name, functions):
    result = []
    for l in functions:
        if l.startswith(name + ' '):
            result.append(l[len(name)+1:])

    if name.startswith('rt_') and len(result) == 0:
        return get_apropos(name[3:], functions)

    return result

total = 0
empties = []
for i in range(350):
    try:
        syscname = subprocess.check_output(['ausyscall', str(i)], stderr=subprocess.STDOUT).strip()
    except subprocess.CalledProcessError, err:
        break

    total += 1
    try:
        manpage = subprocess.check_output(['man', '3' if 60 == i else '2', syscname], stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError, err:
        manpage = ''

    functions = get_apropos(syscname, get_functions(get_synopsis(manpage)))
    if len(functions) == 0:
        empties.append((i, syscname))
        continue
    print i, syscname
    for l in functions:
        print '\t', l

print '='*10, 'EMPTY SYSCALLS {0}/{1}'.format(len(empties), total), '='*10
for emp in empties:
    print emp[0], emp[1]

下面是输出:

0 read
    ssize_t read(int fd, void *buf, size_t count);
1 write
    ssize_t write(int fd, const void *buf, size_t count);
2 open
    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);
3 close
    int close(int fd);
4 stat
    int stat(const char *pathname, struct stat *buf);
5 fstat
    int fstat(int fd, struct stat *buf);
6 lstat
    int lstat(const char *pathname, struct stat *buf);
7 poll
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
8 lseek
    off_t lseek(int fd, off_t offset, int whence);
9 mmap
    void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
10 mprotect
    int mprotect(void *addr, size_t len, int prot);
11 munmap
    int munmap(void *addr, size_t length);
12 brk
    int brk(void *addr);
... ... (此处省略几百行)
========== EMPTY SYSCALLS 22/333 ==========
156 _sysctl
181 getpmsg
182 putpmsg
183 afs_syscall
184 tuxcall
185 security
214 epoll_ctl_old
215 epoll_wait_old
221 fadvise64
236 vserver
262 newfstatat
270 pselect6
283 timerfd
289 signalfd4
290 eventfd2
302 prlimit64
305 clock_adjtime
323 userfaultfd
329 pkey_mprotect
330 pkey_alloc
331 pkey_free
332 statx

发表评论

电子邮件地址不会被公开。 必填项已用*标注