Quantcast
Channel: The Old New Thing
Viewing all articles
Browse latest Browse all 24428

re: Understanding the classical model for linking: You can override an LIB with another LIB, and a LIB with an OBJ, but you can't override an OBJ

$
0
0

///

/// printf.c provides "printf, dummy_printf_do_f_impl, printf_do_f_fp"

///

void dummy_printf_do_f_impl()

{

 printf("Uh oh - you used %%f but didn't use the float keyword anywhere!!"); abort();

 // alternatively, you could dynamically load the floating point library here, for example to handle the case where the main program's "printf" has been passed a float from a plugin.

}

extern FARPROC printf_do_f_fp = &dummy_printf_do_f_impl;

... printf(...)

{

 case "f": printf_do_f_fp();

}

///

/// flt.lib provides __fltused, requires _printf_fp_impl

///

int __fltused;

void* force_load_printf_fp = &actual_printf_do_f_impl;

///

/// printf_fp.c in printf_fp.lib (provides _printf_fp_impl)

///

printf_do_f.c requires __fltused

FARPROC printf_do_f = NULL;

void actual_printf_do_f_impl()

{

 printf("Here's your float: ", ...etc...);

}

extern

class InitMagicNumber

{

InitMagicNumber()

{

   printf_do_f = &actual_printf_do_f_impl;

}

}

_printf_fp_impl;

// Now follow through the compilation of main.c (main() { printf("This should return an error: %f"); }

main.c requires printf, provided by printf.lib. printf.lib doesn't require any printf_fp stuff, so that's not loaded. Now when printf is called, it does normal printf behaviour until it hits %f, when it then calls printf_do_f_fp, which is a symbol in printf.lib defined to point to dummy_printf_do_f_impl, which triggers an error.

// Now follow through the compilation of main_fp.c (main() { printf("This should work: %f", 0.0f); }

main_fp.c uses floating point, so automatically requires __fltinit. It calls printf, so we load printf.lib. printf.lib doesn't use printf_fp, so that doesn't automatically trigger a load there, but we're now left with one unresolved symbol: __fltinit. __fltinit is defined in flt.lib, which requires the symbol _printf_fp_impl, defined in printf_fp.lib. This requires the printf_do_f_fp symbol which resolves to the definition in printf.lib. printf_fp.lib also has a "magic" variable and the linker will add its constructor to the list of CRT initialization functions.

Now when main_fp.c runs, the CRT will call all of the constructors in turn, including the InitMagicNumber magic class, which sets printf_do_f (from printf.lib) to point to actual_printf_do_f_impl. The CRT eventually calls main who calls printf, who eventually calls the deref of printf_do_f_fp, which now takes it to actual_printf_do_f_impl.

Result: printf floating point code is only brought along for the ride if you actually use floating point (or are dumb enough to include an extern int __fltused) somewhere in your code. Otherwise, a dummy implementation of printf will handle the case where %f is used on a non-floating-point program, which can either load the expensive fp library, or can explode, or can return "0.0" or whatever.


Viewing all articles
Browse latest Browse all 24428

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>