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.
First is to use macros. For examples, if you want to override pipe(), then you can write the codes like this.
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.
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,
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
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 ?
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.
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.
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.
If you want to use ld linker, you should create an import library from a .imp file.
First, create a .imp file like this.
Here, ';' is one line comment.
And do these.
Using import simplifies codes although it requires additional files.
/// -----
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 ?
- Load a kLIBC dll such as libc066.
- Query an entry point of the function to be alias.
- Alias a kLIBC function.
- 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.
/// -----
댓글
댓글 쓰기