See below.
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of David Craig
Sent: Wednesday, December 03, 2008 4:53 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] unresolved external symbol _atexit
The driver object has been taken over by NDIS. It no longer belongs to us.
Device object creation is done in the port driver and not in the miniports.
Yes, it could be workable in a ‘normal’ driver. In the NDIS world, we don’t
own the driver object and it is passed to the NDIS registration call. That
is the only time we touch it. None of the normal setting up of function
pointers occurs. I realize you know this, but someone reading might be
misled.
****
Actually, I’ve never worked in the NDIS world, so no, I didn’t know it. And
under those conditions, the use of a global to hold the information would be
required.
****
I think much of this occurs with KMDF also which is why there is a special
way to register with KMDF that keeps it from trying to own the driver
object. A sample NDIS driver is supposed to show how this is done so you
have an upper NDIS edge, but a lower KMDF edge. I have not looked at that
sample since we only have our hardware below us and no other driver is
called, but it is useful for a USB network adapter.
I do agree with what you say about some that think passing parameters down
the stack is expensive. The solution is to keep the parameters in the
device extension where everyone can have access to them. Those that are
unique to a specific request then can be packaged into one structure to
reduce the amount of data being pushed on the stack. I think the prefast
default stack limit test of 1KB is a little excessive for many drivers. I
like to keep a single function’s stack usage to below 256 bytes and even
that is a little high for legacy file system filters where reentrancy is a
possibility due to other filters. That is a big advantage to the minifilter
model.
****
Stack space is the limitation; time is not (when a function call/return
takes 350ps, that is 175ps call and 175ps return, on a 350ps (2.8GHz)
pipelined superscalar architecture, with each parameter push costing a
whopping 175ps, it is hard to sustain the argument that function calls are
expensive. The alternative is to allocate a block of storage which is the
parameter block holding all relevant information, and pass only a pointer to
that struct. Since this involves storage allocation, which *is* expensive,
the alternatives are a block of preallocated objects (sort of dangerous) or
a lookaside list.
****
However, if this world of device drivers were easy, it wouldn’t be as much
fun or pay as much. Even with miniports, minifilters, and KMDF it is still
far more difficult than writing C# for small user programs. The
interactions between other drivers both OS and third party, can keep you
learning and thinking.
****
If we didn’t get challenged, life would get real dull real fast
joe
****
“Joseph M. Newcomer” wrote in message
news:xxxxx@ntdev…
See below.
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of David Craig
Sent: Wednesday, December 03, 2008 2:41 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] unresolved external symbol atexit
There are justifications for using globals, but generally you are correct in
that they are misused. If you have a crash dump callback for secondary data
and your driver supports multiple instances of your hardware, using a global
is the only way to find all your device structures.
Wouldn’t this be doable simply by finding the DRIVER_OBJECT and walking its
list.?
Since you are running at HIGH_LEVEL in the callback, you only need to see if
the global linked list was in the process of being updated and if not you
can grab the information you need to add to the dump. Some of the WMI
performance information might be driver oriented vice device specific, so
that can be another justification.
Yes, the driver-level stuff is one of the justifications. Unfortunately,
the drivers I’ve seen aren’t at all that sophisticated, they just declare
global variables “So I don’t have to keep passing parameters down the stack,
because that’s expensive!” ARGH!
Most of the crashes are from concurrent access to these variables. The
usual: “We plugged a second device in, and every time we do this it
bluescreens”. I’m sure you’ve all seen these. Reading these drivers is
about as much fun as chewing on lemons for several hours.
I had once case where they said “The system would just hang, and someone
told us we were getting deadlock, so we started removing spin locks until it
stopped deadlocking”.
Please be specific as to the problems you think local static values can
cause. I see some use for them, but don’t think I have done it in a driver
recently. I do see a place where a table is needed and it should not be
exposed via a global. That table is initialized at compile time and not
modified during execution, so I think it is acceptable. It is an easy way
to do a type of data & code binding as is done in C++, but in standard C.
It could be done with a global, but it shows its usage and restricts its
access easily.
****
The usual one is “retaining state for the next call”. In a preemptive,
multiprocessor, preemptive threading environment, with the possibility of
multiple instances of the device, this is extremely dangerous; these are all
old C application programmers to whom the concepts of “threads” and
“concurrency” are a foreign language. When I explain race conditions to
them, they assure me that these are not possible because.and go off on some
single-threaded explanation that cannot possibly work. What I tell my
students is “If you have a global variable or local static variable that is
not preceded by the word ‘const’, consider that you have probably made a
coding error. When you get sophisticated enough that you know what you are
doing, you will realize that there are two exceptions: write-once constants
and true global state” In our book on device drivers, we actually chose a
DDK example driver for discussion that uses genuine global state, among
other reasons so we could talk about the need for and proper management of
global state, but in general, drives with global state and static local
variables represent the first or second driver the coder has written, and
they are still thinking single-threaded application-level code.
So when I see a global variable, the first thing I do is see how it is being
(mis)used, and I’m almost always right about the (mis) part.
Of course, they way they solve this is to correct it by stuffing it into the
device extension, without actually solving the fundamental race conditions
that can occur. About half the time, they win by accident, because once it
is no longer shared across all device instances, there is not a problem.
But sometimes it is passive/dpc/isr level concurrency that nails them.
Either the algorithm is fundamentally wrong, or there is no synchronization.
The problem is that they don’t have a good “ownership” model of the data
(e.g., the buffer pointer in the extension is either “owned” by the dequeue
handler, or the ISR, or the DPC, sequentially and never concurrently, and it
will work. Or there is an overlap, and I still get “but a++ is an atomic
operation, so cannot fail!” (How they determined this is unknown, since I
rarely see the compiler generating an INC instruction.but it was once true
on a couple machines in the past [PDP-11 and Vax] so someone probably passed
this on as “urban legend”).
There’s nothing wrong with const and pseudo-const (write once at
initialization and never change) data, but that’s not where I see the
problems.
joe
“Joseph M. Newcomer” wrote in message
news:xxxxx@ntdev…
Which raises the issue: why are there global objects in a driver? This is
extremely risky (in general, most people who declare global objects are C
application programmers who are not aware of the numerous problems this
creates in a driver-and for that matter, they shouldn’t have been global
objects anyway, even in C, but most people are prone to using them). Every
driver that I have ever reviewed that had a global declaration (other than
the write-once pseudo-const of saving the DriverEntry path value) got it
wrong. (The next step down is declaring local static values in functions.a
really dangerous practice much beloved by C programmers)
joe
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Skywing
Sent: Wednesday, December 03, 2008 12:35 PM
To: Windows System Software Devs Interest List
Subject: RE: [ntdev] unresolved external symbol _atexit
It is also used for implicitly generated code for global constructors and
destructors as previously mentioned.
Check for global instances of class objects.
- S
_
From: Joseph M. Newcomer
Sent: Tuesday, December 02, 2008 22:29
To: Windows System Software Devs Interest List
Subject: RE: [ntdev] unresolved external symbol atexit
atexit is a user-mode library call. Look it up in the MSDN (atexit). This
is used to create user-library exit calls in the C runtime library. So you
are probably misusing C++ in some fashion to cause it to call a CRT
user-level library call.
joe
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of A P
Sent: Tuesday, December 02, 2008 10:06 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] unresolved external symbol _atexit
hello folks,
i am geting this weird linker error when i try building the driver. any
clues?
the driver has c++ classes in it, but no dynamic allocation/dealloc.
thanks
AP
—
NTDEV is sponsored by OSR
For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars
To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer
–
This message has been scanned for viruses and
dangerous content by http:</http:> MailScanner, and is
believed to be clean.