jacob navia said:
BGB / cr88192 a écrit :
That's why I was forced to rewrite printf.
ok.
Sure, writing the runtime is a big mess. It is very difficult to do.
I once wrote a C runtime, but this was for a standalone OS, and the result
was very buggy...
Very easy. You need just to change the linker, and put your static
library before the import library of crtdll.dll Then you rewrite only
the functions you need to replace.
this (simple patching), however, would be very problematic to do on a
project-wide scale, where each component is built independently (its own
Makefiles, ...), and where the project is built of many separate DLL's...
to be made to work, this patch library would need to be specifically
referred to almost everywhere.
for a name-mangled scheme, it is a lot less of a mess, since then one only
needs to modify the name resolution, AKA, to search for the mangled name
prior to the actual name.
granted, it is still a little bit of a hack, and there are special cases in
my framework where this behavior would need to be inverted (namely, in cases
where "proxying" is used, yet the 'proxy' should still resolve to the
mangled version before the actual version).
add, may add support for a 'version' as well,
"BSRT1__printf"
"BSRT2__printf"
which would mostly effect resolution order (higher numbers checked before
lower ones).
I will probably add the hack to the OS-level resolver, essentially avoiding
most of the issues by pretenting that it is the DLL subsystem doing this
hackery (it would then be largely invisible within the framework's dynamic
linker).
Well, in 32 bits you have to generate a function call anyway, so it
doesn't
make any difference. In 64 bits there are no function calls and the
code can be generated directly.
granted, I often do this already.
however, the "function calls" in 32 bit mode (and for relevant operations in
64 bit mode, such as division), are not ordinary functions, but rather are
specialized ASM helpers (typically, using very specialized calling
conventions, and usually with a rule of being "register opaque").
the cost though, is in cases where these sorts of helpers actually need to
make a general-purpose function call, which essentially forces them to save
and restore any registers which may be potentially modified by the call
(essentially, all the caller save registers...).
this case is not an issue for my arithmetic helpers (which are
self-contained), but may be an issue for my OO and TLS helpers (the former
of which have been 'helped' by passing in a partial register-allocator
state, allowing them to know which registers they don't need to save).
[snip]
originally, I passed them directly on the stack in Win64, but then
discovered that this was apparently incorrect, and these issues have been
internally addressed via kludges (something I call "vrefs" or "virtual
references", which are like a C++ references, only added silently by the
compiler to gloss over the calling convention...).
Well, this is easy... You just copy the object in the stack and pass
a pointer to that stack area in some register. Note that you should copy
since C passes by value!
I am dealing with the Win64 calling convention, which apparently passes
structs by reference.
the only thing then present in the passed arguments is, then, the pointer.
the hack is then partially, that I have to allocate a "tvar" (temporary
variable), to hold the value of the passed struct (or XMM register, ...),
and then the 'value' is replaced implicitly by a 'vref to the value'.
sadly, my compiler is currently too stupid to know how to properly free the
tvar afterwards, but oh well... (this could waste a lot of stack space for a
single function containing a large number of calls directly passing structs
or SIMD types).
struct foo {int a,b,c,d;};
struct foo Foo;
void fn(struct foo f);
fn(Foo);
Here fn should work with a COPY of the structure Foo.
theoretically, yes, but AFAICT Win64 does not do it this way, instead,
leaving the memory for the struct in the caller (in a temporary piece of
memory), and modifying it in-place (rather than, say, making a local copy in
the callee).
so, if you pass Foo, the caller will make a copy of the struct, and pass the
pointer to this copy, and the callee will modify the struct as-given (so, if
directly passes a pointer to Foo, one is liable to get their struct
thrashed?...).
(intuitively, I would think instead the callee 'should' make the copy, vs
the caller, but oh well...).
I have not fully investigated the matter (such as whether or not this is a
defined, or at least 'safe', practice), but this seems to be the practice...
this is in contrast to Win32 + cdecl, which passes the entire structure
directly on the stack (vs a pointer).
note that I have also seen MSVC moving memory around via 'rep movsb' when
clearly 'movaps' would be safe, and a much faster alternative (I don't
personally give MSVC's optimizer much credit after seeing a lot of the code
it spews out, or at least for x64...).