OS/2 codes: How to override standard functions in kLIBC

Sometimes there is a time when it is needed to override the functionality of some functions. To do this, at least two ways can be considered.

Macros


First is to use macros. For examples, if you want to override pipe(), then you can write the codes like this.

1
2
3
4
5
6
7
8
9
10
#include <io.h>

int my_pipe(int *ph)
{
    some_codes;

    return (pipe)(ph);
}

#define pipe(ph) my_pipe(ph)


At line 7, a parenthesis is used to prevent pipe() from being expaned to my_pipe() by a macro.
This way has an advantage that this can be applied to all the functions. But this has a disadvantage that this can be applied only locally. If you want to apply globally, then you should create a header for a macro and all the sources should include that header.

Alias


The other way is to use alias. kLIBC provides aliases for standard functions, which do not have a leading underscore. Standard functions have at least two names. One is a name in a header, the other is the name having _std_ prefix. For examples, pipe() in io.h has pipe() and _std_pipe(). So if you want to override pipe(), then write the codes for it in pipe(). And if you want to call original pipe(), then use _std_pipe().

The above codes can be written like this,

1
2
3
4
5
6
7
8
9
10
#include <io.h>

int _std_pipe(int *ph);

int pipe(int *ph)
{
    some_codes;

    return _std_pipe(ph);
}


You should declare a prototype for a standard function like line 3. This way is applied globally. and does not require additional headers. In addition, there is no need to modify other sources. Just including a standard header and linking with this file is enough. But this can be applied only to standard functions.

/// ----- 2016/10/12

Alias with DLL


Another way is to use a function of a dll directly. kLIBC is a run-time dll. Therefore it's possible to get entry points of exported functions of kLIBC

This way is similar to 'alias'. But unlike 'alias', this way allows to alias non-standard functions as well such as _beginthread().

What is the way ?

  1. Load a kLIBC dll such as libc066.
  2. Query an entry point of the function to be alias.
  3. Alias a kLIBC function.
  4. Call a function pointer got from kLIBC dll where it is needed.

When querying an entry point, you should use _std_ prefix for standard functions. Non-standard functions do not have _std_ prefix. And you should prepend _ to both cases again.

For examples, use __std_pipe for pipe not _std_pipe. And __beginthread for _beginthread not __std__beginthread.

Here is the example code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/** @file override1.c
 */


#define INCL_DOS
#include <os2.h>

#include <io.h>
#include <fcntl.h>

/**
 * Query an entry point of kLIBC APIs
 *
 * @param procName[in] kLIBC API name to query
 * @param ppfn[out] where to store an address of an entry point
 * @return 0 on success or -1 on error
 */

static int queryCDllProcAddr( const char *procName, PPFN ppfn )
{
    static HMODULE hmod = NULLHANDLE;

    /* Load libc066.dll only once */
    if( hmod == NULLHANDLE )
    {
        char szFailed[ 256 ];

        if( DosLoadModule( szFailed, sizeof( szFailed ), "libc066", &hmod ))
        {
            hmod = NULLHANDLE;

            return -1;
        }
    }

    /* Query an entry point of a requsted kLIBC API */
    if( DosQueryProcAddr( hmod, 0, procName, ppfn ))
        return -1;

    return 0;
}

/**
 * Open unnamed pipes in binary mode
 */

int pipe( int *two_handles )
{
    static int ( *cdll_pipe )( int * ) = NULL;

    int rc;

    /* Query an entry point of pipe() only once */
    if( cdll_pipe == NULL )
        queryCDllProcAddr("__std_pipe", &cdll_pipe );

    rc = cdll_pipe( two_handles );
    if( rc == 0 )
    {
        /* Set pipe handles to binary mode */
        setmode( two_handles[ 0 ], O_BINARY );
        setmode( two_handles[ 1 ], O_BINARY );
    }

    return rc;
}

int main( void )
{
    int ph[ 2 ];

    pipe( ph );

    printf("ph[ 0 ] mode = %x(%x)\n", setmode( ph[ 0 ], O_TEXT ), O_BINARY );
    printf("ph[ 1 ] mode = %x(%x)\n", setmode( ph[ 1 ], O_TEXT ), O_BINARY );

    close( ph[ 0 ]);
    close( ph[ 1 ]);

    return 0;
}


However, this is more or less complex because of loading kLIBC DLL dynamically. To simplify this, you can use .def file instead.

Here is the example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/** @file override2.c
 */


#define INCL_DOS
#include <os2.h>

#include <io.h>
#include <fcntl.h>

int cdll_pipe( int * );

/**
 * Open unnamed pipes in binary mode
 */

int pipe( int *two_handles )
{
    int rc;

    rc = cdll_pipe( two_handles );
    if( rc == 0 )
    {
        /* Set pipe handles to binary mode */
        setmode( two_handles[ 0 ], O_BINARY );
        setmode( two_handles[ 1 ], O_BINARY );
    }

    return rc;
}

int main( void )
{
    int ph[ 2 ];

    pipe( ph );

    printf("ph[ 0 ] mode = %x(%x)\n", setmode( ph[ 0 ], O_TEXT ), O_BINARY );
    printf("ph[ 1 ] mode = %x(%x)\n", setmode( ph[ 1 ], O_TEXT ), O_BINARY );

    close( ph[ 0 ]);
    close( ph[ 1 ]);

    return 0;
}


1
2
3
4
; @file override2.def
NAME override2 WINDOWCOMPAT
IMPORTS
    _cdll_pipe = libc066.__std_pipe


IMPORTS field defines the functions to import from kLIBC dll.

To build the above example, you must use -Zomf because ld linker does not support IMPORTS field of .def file.

gcc -Zomf override2.c override2.def

If you want to use ld linker, you should create an import library from a .imp file.

First, create a .imp file like this.

; @file override2.imp
_cdll_pipe libc066 __std_pipe ?  

Here, ';' is one line comment.

And do these.

emximp -o override2.a override2.imp
gcc override2.c override2.a

Using import simplifies codes although it requires additional files.


/// -----

댓글

이 블로그의 인기 게시물

토렌트: < 왕좌의 게임 > 시즌 1 ~ 시즌 8 완결편 마그넷

토렌트: NGC < 코스모스 > 우리말 더빙 전편(1편~13편) 마그넷

토렌트: < 스타워즈 > Ep.1 ~ Ep.6 마그넷