 |
 |
|
 |
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
So here's a question: how does linking actually work?
I mean, I understand what it *does*. For some reason†, the C language
allows you to write functions that call functions that don't exist. This
leaves you with an object file containing unresolved references. The
linker then resolves these references.
[ † I'm guessing the "some reason" boils down to "the machine we
designed this for only has 2KB of memory implemented as a mercury delay
line" or some such stupidity... ]
But how does that actually *work*? The C compiler transforms C source
code into executable object code. Basically, the object file contains
(among other things) raw machine code, which the processor knows how to
execute. Calling a subroutine is implemented as an unconditional jump
op-code. If you know the jump target, then by all means, fill in the
target address. But if the target hasn't been resolved yet... how do you
fit the entire symbol name into 32 bits?
(32 bits is only 4 characters. And C++ in particular seems determined to
transform even the most trivial function call into an 8-mile symbol name!)
Presumably the object file contains some metadata too. Stuff that tells
you it *is* an object file, what the target processor is, what symbols
it exposes publicly, etc. But I'm not sure how unresolved function calls
are implemented.
For that matter, how does the linker sort all this out? Does it actually
load the entire final binary into memory while it untangles it? Or does
it somehow manage to incrementally build the file on disk? [I guess it's
perhaps implementation-defined...] My Linux box is sitting here with
16GB of RAM, and can probably handle holding the whole 0.2MB program in
RAM at once. The original 2KB system that C was designed for? Not so much.
For that matter, is it possible to store *data* in an object file? (As
opposed to executable code.)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
hi,
On 28/05/2016 08:47, Orchid Win7 v1 wrote:
> So here's a question: how does linking actually work?
can't answer all of your questions, but here goes:
you're right about the jump-table. google / search docs for "GOT"
(global offset table).
the .o files aren't "raw machine code". afaik, GCC uses an intermediary
language ("gimple") which allows creating programs/libs where different
TUs may be written in different languages (C, Ada, etc). see the docs
on the '-flto' (link time optimisation) compiler and '-Wl,-flto' linker
options.
jr.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Le 28/05/2016 09:47, Orchid Win7 v1 a écrit :
> So here's a question: how does linking actually work?
>
> I mean, I understand what it *does*. For some reason†, the C language allows you
to write functions that call functions that don't exist. This leaves you with an
object file containing unresolved references. The linker then resolves these
references.
>
> [ † I'm guessing the "some reason" boils down to "the machine we designed this for
only has 2KB of memory implemented as a mercury delay line" or some such stupidity...
]
>
> But how does that actually *work*? The C compiler transforms C source code into
executable object code. Basically, the object file contains (among other things) raw
machine code, which the processor knows how to execute. Calling a subroutine is
implemented as an
> unconditional jump op-code. If you know the jump target, then by all means, fill in
the target address. But if the target hasn't been resolved yet... how do you fit the
entire symbol name into 32 bits?
>
> (32 bits is only 4 characters. And C++ in particular seems determined to transform
even the most trivial function call into an 8-mile symbol name!)
>
> Presumably the object file contains some metadata too. Stuff that tells you it *is*
an object file, what the target processor is, what symbols it exposes publicly, etc.
But I'm not sure how unresolved function calls are implemented.
>
> For that matter, how does the linker sort all this out? Does it actually load the
entire final binary into memory while it untangles it? Or does it somehow manage to
incrementally build the file on disk? [I guess it's perhaps implementation-defined...]
My Linux box is
> sitting here with 16GB of RAM, and can probably handle holding the whole 0.2MB
program in RAM at once. The original 2KB system that C was designed for? Not so much.
>
> For that matter, is it possible to store *data* in an object file? (As opposed to
executable code.)
At the beginning was the sequence of instructions, as read by the processor.
It was just painful to write for the humans, so Assembly was done: a mnemonic was used
instead of number, and began the era of symbols and labels.
And it was fine... but each processor came with its own assembly. It was not portable
and humans had to do it all over again when a new processor was made.
Then came C. The usual C compiler was in charge of generating the suitable assembly (
that's why .c can still be transformed in .s before becoming a .o )
C came with its own way to transform its symbols into the symbols supported by
assembly : mangling.
C++, because it allows even more characters in its symbols, has its own mangling too,
more complex, and not always compatible with the C mangle.
That's why you can encounter
extern "C" {
/* C code here */
}
statements: to inform the mangler to use the C version instead of the C++ one.
In classical assembly, symbols are mostly labels, and labels are position (address ?
dangerous shortcut) in file. They can be limited to 6 uppercase characters (very old)
or allow more length (32 was another limit).
What is found at a label can be data or code. And because humans are lazy, there is
even a special kind of data: data declared by length but without value (0 is assumed,
but not always).
So 3 kinds of label: initialized data, uninitialized data and code. (that's called
"section" in linker's jargon)
The linker is in charge of organizing the code and initialized data, grouping the
various sections and replacing the label with actual position (which might be absolute
or relative) for the processor.
The "initialization" of bss (unitialized data) is left to the program start code: if
you transfer a program with a huge bss section, the transfer is short because only
length is in the file, not the actual number of bytes. ("need a segment of 64k bytes"
is smaller than
providing the actual 64k bytes)
So far so good... just that with current linker, there is about 9 or more sections
instead of just 3.
A .o file contains the transcription of a .s : data, code and a bit of extra data
about the exported label, and imported label. (XREF and XDEF are some assembly's
instructions for that purpose... for some languages)
A linker, when making a C or C++ program, starts with an implicit (very well hidden,
in an implicit library or .o of the link chain) need of a main() symbol.
For every .o on the command line of the linker, the linker accumulates the needed &
provided symbols.
For every .a on the command line of the linker (via -lxxx, libxxx.a is explored): open
the library, extract all still needed symbols that are found (that might trigger the
addition of more needed symbols), close the library and forget forever about it, move
to next
library in the order of the command line.
If all symbols (including the main() ) have been found, it's a success, and all
symbols can be replaced with position in file/memory.
Otherwise, complains about unresolved symbols.
For shared library (.so) instead of static library (.a), the processing is the same,
but the code is not extracted from the library: only the name of the library is
stored. The start code of the program will load the shared library and try to perform
the relocation
(replacement of labels with position).
A function call is just: stack the parameters according to the ABI, jumps to the label
of the mangled function's name, on return extract the return value from the stack and
dispose of the previously stacked parameters. That's why C++ absolutly wants a
declaration of the
function it would call: to stack the parameters correctly in the generated assembly.
When unresolved, all that remains after that is a XREF of the mangled function name,
for the linker to solve.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Am 28.05.2016 um 14:03 schrieb Le_Forgeron:
> Then came C. The usual C compiler was in charge of generating the suitable assembly
( that's why .c can still be transformed in .s before becoming a .o )
>
> C came with its own way to transform its symbols into the symbols supported by
assembly : mangling.
Uh... no, generally C does not have any name mangling. The symbols are
taken as-is. (Microsoft Windows programs being an exception.)
Which is why you can't overload functions in C.
> C++, because it allows even more characters in its symbols, has its own mangling
too, more complex, and not always compatible with the C mangle.
> That's why you can encounter
>
> extern "C" {
> /* C code here */
> }
>
> statements: to inform the mangler to use the C version instead of the C++ one.
Equally importantly, it also informs the compiler about the call
conventions to use (what registers to use for parameters, whether the
caller or the callee is responsible for stack cleanup, and the like).
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Am 28.05.2016 um 09:47 schrieb Orchid Win7 v1:
> But how does that actually *work*? The C compiler transforms C source
> code into executable object code. Basically, the object file contains
> (among other things) raw machine code, which the processor knows how to
> execute. Calling a subroutine is implemented as an unconditional jump
> op-code. If you know the jump target, then by all means, fill in the
> target address. But if the target hasn't been resolved yet... how do you
> fit the entire symbol name into 32 bits?
Simple: You don't.
Instead, you add a table to the library listing all the memory locations
that should hold the address of a given unresolved symbol but for
obvious reasons currently don't.
The linker will later use that table to update those memory locations.
> (32 bits is only 4 characters. And C++ in particular seems determined to
> transform even the most trivial function call into an 8-mile symbol name!)
>
> Presumably the object file contains some metadata too. Stuff that tells
> you it *is* an object file, what the target processor is, what symbols
> it exposes publicly, etc. But I'm not sure how unresolved function calls
> are implemented.
You /could/ look it up... you know, they have this fancy new thing
called the Internet, and search engines and things ;)
> For that matter, how does the linker sort all this out? Does it actually
> load the entire final binary into memory while it untangles it? Or does
> it somehow manage to incrementally build the file on disk? [I guess it's
> perhaps implementation-defined...] My Linux box is sitting here with
> 16GB of RAM, and can probably handle holding the whole 0.2MB program in
> RAM at once. The original 2KB system that C was designed for? Not so much.
[Your guess is correct.]
> For that matter, is it possible to store *data* in an object file? (As
> opposed to executable code.)
Absolutely. See the `source/base/font/*.cpp` files in the POV-Ray source
code for examples.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Am 28.05.2016 um 09:47 schrieb Orchid Win7 v1:
> I mean, I understand what it *does*. For some reason†, the C language
> allows you to write functions that call functions that don't exist. This
> leaves you with an object file containing unresolved references. The
> linker then resolves these references.
>
> [ † I'm guessing the "some reason" boils down to "the machine we
> designed this for only has 2KB of memory implemented as a mercury delay
> line" or some such stupidity... ]
No. The "some reason" is that you don't want to have to recompile your
entire project just because some minor modification in a single obscure
C source file has caused all your memory addresses to shift.
Therefore, each and every (!) C source file is first translated
("compiled") into an address-independent (*) object file, and in a later
step all the object files in your project are combined ("linked") into a
single executable with fixed addresses.
(* To achieve address independency, a similar approach is used as for
external symbols: A table is included in the object file listing each
and every memory location that will have to hold an absolute address in
the executable, but in the object file only holds an offset relative to
the object file's "payload".)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Orchid Win7 v1 <voi### [at] dev null> wrote:
> Presumably the object file contains some metadata too. Stuff that tells
> you it *is* an object file, what the target processor is, what symbols
> it exposes publicly, etc. But I'm not sure how unresolved function calls
> are implemented.
Even the final executable file isn't a fixed blob of machine code.
Linking happens also when executing such an executble file (look up
"dynamic linker").
Executable files contain references to dynamically loadable libraries,
and when you execute such a file, the OS will insert function calls
into said executable to point to whichever dynamically loadable library
it needs (which the OS also loads or, most usually, is already loaded
into memory because most of everything else needs it too.)
The idea with dynamically loadable libraries is, of course, to save
memory and increase efficiency. Since 99.9% of all executables use
the same system functions, it's more efficient to have them all share
the one and same library in memory than to statically link all those
megabytes of system library code into every single executable.
When object files refer to other object files, or to statically linked
libraries, a similar process happens, but at linking time, rather than
at runtime.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 29/05/2016 04:39 AM, clipka wrote:
> Am 28.05.2016 um 09:47 schrieb Orchid Win7 v1:
>
>> But how does that actually *work*? The C compiler transforms C source
>> code into executable object code. Basically, the object file contains
>> (among other things) raw machine code, which the processor knows how to
>> execute. Calling a subroutine is implemented as an unconditional jump
>> op-code. If you know the jump target, then by all means, fill in the
>> target address. But if the target hasn't been resolved yet... how do you
>> fit the entire symbol name into 32 bits?
>
> Simple: You don't.
>
> Instead, you add a table to the library listing all the memory locations
> that should hold the address of a given unresolved symbol but for
> obvious reasons currently don't.
>
> The linker will later use that table to update those memory locations.
Oh, I see. So, what, the jump op-code says to jump to address zero, and
the object metadata tells the linker which bytes to change?
> You /could/ look it up... you know, they have this fancy new thing
> called the Internet, and search engines and things ;)
Don't you start. I've already spent a day looking at the output of nm,
objdump and readelf. :-P The manpages tell you what all the switches do,
but I still have no idea what .text is supposed to mean.
>> For that matter, is it possible to store *data* in an object file? (As
>> opposed to executable code.)
>
> Absolutely. See the `source/base/font/*.cpp` files in the POV-Ray source
> code for examples.
Hmm. Interesting.
extern const unsigned char font_timrom[36936]={...
I didn't think you could make an array const. (Wouldn't that just mean
the array pointer itself is constant? Not the array it points to.) And I
thought extern means "this is declared somewhere else"?
[Also... Heh, do you know, when you said it, I was thinking I'd be able
to browse the source online somewhere. Silly me...]
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 29/05/2016 06:37 AM, Warp wrote:
> Executable files contain references to dynamically loadable libraries,
> and when you execute such a file, the OS will insert function calls
> into said executable to point to whichever dynamically loadable library
> it needs (which the OS also loads or, most usually, is already loaded
> into memory because most of everything else needs it too.)
I don't have any direct experience with that.
I was *about* to say that I've only done it with AmigaOS - but that's
not quite right. What I was *actually* looking it is calling *the OS*,
which is a bit different.
The way AmigaOS does it, memory address 0x00000004 holds a pointer to
the function table for exec.library. Every function in the library has a
known index in that table, so by adding your index to the address
pointed to, you get a function pointer to the actual library function
that you want. Now since exec.library is the one that contains the
functions to load *other* libraries, from here you can open any other
library you want. This similarly returns a function table base pointer,
which you can use in the same way.
Of course, exec.library is in ROM, as are most of the low-level system
libraries [including the entire GUI]. But you don't need to care about
that. Just call OpenLibrary(), and it'll load from disk if required, and
ultimately give you back a base pointer.
Presumably any self-respecting protected-mode OS does it differently. In
particular, calling the kernel presumably implies a transition to
ring-0, and I don't remember how x86 does that exactly. (From what I
dimly recall, you purposely trigger a kind of software interrupt, but
I'm not sure how you designate what function you're trying to call.)
From what I can tell, C code does not call the Linux kernel. C code
calls glibc, which then calls the kernel on your behalf. (As evidenced
by several manpages that describe a glibc function and a kernel function
of identical name but subtly different behaviour...)
> The idea with dynamically loadable libraries is, of course, to save
> memory and increase efficiency. Since 99.9% of all executables use
> the same system functions, it's more efficient to have them all share
> the one and same library in memory than to statically link all those
> megabytes of system library code into every single executable.
This gets entertaining when you have one program that uses a dozen
libraries that nobody else is using. Or when every single program on the
system uses a different version of the same library, so they all supply
their own version of it. Then again, if old code doesn't work with a
newer version of some dynamic library, who's fault is that?
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Le 29/05/2016 09:48, Orchid Win7 v1 a écrit :
> Then again, if old code doesn't
> work with a newer version of some dynamic library, who's fault is that?
That's why the version numbers of a dynamic library is usually part of the name, even
if a link is available from the name without version.
Of course, it took some time for folks to understand why it is better than a
msvcrt.dll (same name for different versions).
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Am 29.05.2016 um 09:26 schrieb Orchid Win7 v1:
>> The linker will later use that table to update those memory locations.
>
> Oh, I see. So, what, the jump op-code says to jump to address zero, and
> the object metadata tells the linker which bytes to change?
Yup.
>> You /could/ look it up... you know, they have this fancy new thing
>> called the Internet, and search engines and things ;)
>
> Don't you start. I've already spent a day looking at the output of nm,
> objdump and readelf. :-P The manpages tell you what all the switches do,
> but I still have no idea what .text is supposed to mean.
I haven't ever looked at any of those, but when I hear "text" in the
context of linking, I think of the "text segment", which is where all
the instructions will be stored (aka "code segment" as Intel would call it).
>>> For that matter, is it possible to store *data* in an object file? (As
>>> opposed to executable code.)
>>
>> Absolutely. See the `source/base/font/*.cpp` files in the POV-Ray source
>> code for examples.
>
> Hmm. Interesting.
>
> extern const unsigned char font_timrom[36936]={...
>
> I didn't think you could make an array const. (Wouldn't that just mean
> the array pointer itself is constant? Not the array it points to.) And I
> thought extern means "this is declared somewhere else"?
The "const unsigned char FOO[...]={...}" means that FOO is an array of
constant unsigned chars.
Without the "const", it would mean that the array, although initialized
at startup, could be tampered with later.
As for the "extern" that keyword does /not/ denote that the thing is
defined(!) "somewhere else", but that the thing "has external linkage",
i.e. is shared by multiple translation units (= .c or .cpp files).
Some library authors seem to go to great lengths to declare stuff as
"extern" when their header files are included from code that uses the
library, but leave out that "extern" when included from the library
itself; I currently don't know why they do that -- according to the C
and C++ standards this can only work because most stuff is implicitly
"extern" anyway unless explicitly declared "static".
It so happens that "const" stuff is one of the few things implicitly
"static" unless explicitly declared "extern".
> [Also... Heh, do you know, when you said it, I was thinking I'd be able
> to browse the source online somewhere. Silly me...]
Silly you indeed for not figuring out that you can indeed browse the
source code on GitHub ;)
https://github.com/POV-Ray/povray/tree/master/source/base/font
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Am 29.05.2016 um 09:48 schrieb Orchid Win7 v1:
> I was *about* to say that I've only done it with AmigaOS - but that's
> not quite right. What I was *actually* looking it is calling *the OS*,
> which is a bit different.
...
> Presumably any self-respecting protected-mode OS does it differently. In
> particular, calling the kernel presumably implies a transition to
> ring-0, and I don't remember how x86 does that exactly. (From what I
> dimly recall, you purposely trigger a kind of software interrupt, but
> I'm not sure how you designate what function you're trying to call.)
That's pretty simple: You pass a kind of function ID in a particular CPU
register.
For instance, in good old DOS, the sole entry point for all operating
system functions was INT 21h, with the AH register indicating which
function you intended to call.
According to Wikipedia, Linux originally used pretty much the same
principle, except with INT 80h and the AX register.
Windows also uses the AX register to identify the kernel function, but
uses the faster dedicated SYSENTER instruction (introduced with the
Pentium II) instead of a software interrupt. (The drawback is that this
instruction doesn't push a return address onto the stack, so a CALL to a
stub is usually employed.)
Likewise, Linux running on modern machines also uses SYSENTER or the
even newer SYSCALL.
The x86 architecture also provides a mechanism known as a "call gate",
which uses a CALL FAR instruction to a segment specifically set up by
the operating system to redirect the call to an entirely different
address while switching privilege level. This mechanism was used by some
operating systems, but has gone out of style. Not sure whether those
systems used a single call gate and a register to pass a function ID, or
whether they used one call gate per kernel function.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
> The way AmigaOS does it, memory address 0x00000004 holds a pointer to
> the function table for exec.library. Every function in the library has a
> known index in that table, so by adding your index to the address
> pointed to, you get a function pointer to the actual library function
> that you want. Now since exec.library is the one that contains the
> functions to load *other* libraries, from here you can open any other
> library you want. This similarly returns a function table base pointer,
> which you can use in the same way.
>
> Of course, exec.library is in ROM, as are most of the low-level system
> libraries [including the entire GUI]. But you don't need to care about
> that. Just call OpenLibrary(), and it'll load from disk if required, and
> ultimately give you back a base pointer.
>
> Presumably any self-respecting protected-mode OS does it differently. In
> particular, calling the kernel presumably implies a transition to
> ring-0, and I don't remember how x86 does that exactly. (From what I
> dimly recall, you purposely trigger a kind of software interrupt, but
> I'm not sure how you designate what function you're trying to call.)
Not a protected-mode OS, but in RISCOS on ARM there is an instruction
"SWI" that triggers a software interrupt. There are 24 bits spare in
that instruction to specify the routine number (the other 8 to identify
the instruction itself and conditional execution flags etc). Routines
have a name and a number, the OS maintains a list if you can only
remember the name (in fact most compilers/interpreters will allow you to
write the name, and automatically substitute the number to the
instruction). The processor registers r0 up to r8 (IIRC) were passed on
to the SWI handler code and returned, so these were used for parameters
and return values.
It was possible to add your own routines to this system, in fact this is
how almost all 3rd party libraries (eg music players, image converters,
compression software,...) were written. The equivalent of how DLLs are
used today I guess.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 31/05/2016 07:57 PM, clipka wrote:
> Am 29.05.2016 um 09:48 schrieb Orchid Win7 v1:
>
>> I was *about* to say that I've only done it with AmigaOS - but that's
>> not quite right. What I was *actually* looking it is calling *the OS*,
>> which is a bit different.
> ...
>> Presumably any self-respecting protected-mode OS does it differently. In
>> particular, calling the kernel presumably implies a transition to
>> ring-0, and I don't remember how x86 does that exactly. (From what I
>> dimly recall, you purposely trigger a kind of software interrupt, but
>> I'm not sure how you designate what function you're trying to call.)
>
> That's pretty simple: You pass a kind of function ID in a particular CPU
> register.
>
> For instance, in good old DOS, the sole entry point for all operating
> system functions was INT 21h, with the AH register indicating which
> function you intended to call.
That's essentially a direct call to the BIOS itself, isn't it? Or does
MS-DOS actually interact with this somehow? (I realise that MS-DOS is a
very thin "OS", if you can even call it that.)
> According to Wikipedia, Linux originally used pretty much the same
> principle, except with INT 80h and the AX register.
OK...
> Windows also uses the AX register to identify the kernel function, but
> uses the faster dedicated SYSENTER instruction (introduced with the
> Pentium II) instead of a software interrupt. (The drawback is that this
> instruction doesn't push a return address onto the stack, so a CALL to a
> stub is usually employed.)
>
> Likewise, Linux running on modern machines also uses SYSENTER or the
> even newer SYSCALL.
OK, fair enough.
Both IA32 (and later AMD64) are quite short on registers, which is
presumably why the default C calling convention is seemingly via the
stack. In AmigaOS, running on a Motorola 68000 with a dozen registers,
most of this stuff is via register...
> The x86 architecture also provides a mechanism known as a "call gate",
Yeah, I remember reading about that in the IA32 reference manual. I
can't recall the details of how it works though.
> This mechanism was used by some
> operating systems, but has gone out of style.
OK. Sounds like there's not much point remembering then.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Am 01.06.2016 um 18:59 schrieb Orchid Win7 v1:
>> For instance, in good old DOS, the sole entry point for all operating
>> system functions was INT 21h, with the AH register indicating which
>> function you intended to call.
>
> That's essentially a direct call to the BIOS itself, isn't it? Or does
> MS-DOS actually interact with this somehow? (I realise that MS-DOS is a
> very thin "OS", if you can even call it that.)
No; the BIOS uses the same principle, but different interrupts.
BIOS-level disk I/O, for instance, uses INT 13h.
>> The x86 architecture also provides a mechanism known as a "call gate",
>
> Yeah, I remember reading about that in the IA32 reference manual. I
> can't recall the details of how it works though.
Don't worry, the IA32 reference manuals recall the details pretty well ;)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Am 2016-06-02 01:22, also sprach clipka:
> No; the BIOS uses the same principle, but different interrupts.
>
> BIOS-level disk I/O, for instance, uses INT 13h.
I haven't been inside of Windows since ... XP. Do they *still* use
that? I recall an early Linux decision was to get the hell away from
the BIOS asap and stay away. -- Device drivers uber alles.
(I kept waiting for M$ to invent the swap partition, to speed things up
a little.)
I was always annoyed that when M$ bought Seattle Computer Products' DOS,
they didn't rearrange the function numbers before releasing it. It
would have been nice if the disk functions were grouped together for
when referring to The Green Book. Instead, it was obvious that as each
function was added, it just went to the end of the list. So,
open/read/write/close were like 5/6/7/8. Then delete was 23, rename was
54. A real pita for book referring.
--
dik
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Am 02.06.2016 um 10:49 schrieb dick balaska:
> Am 2016-06-02 01:22, also sprach clipka:
>
>> No; the BIOS uses the same principle, but different interrupts.
>>
>> BIOS-level disk I/O, for instance, uses INT 13h.
>
> I haven't been inside of Windows since ... XP. Do they *still* use
> that? I recall an early Linux decision was to get the hell away from
> the BIOS asap and stay away. -- Device drivers uber alles.
On machines without UEFI support, both Windows AND Linux still use the
BIOS... for the very first phase of the boot procedure.
From then on, Windows doesn't look back at the BIOS either. It may keep
the BIOS' assignments of resources (I/O addresses, interrupts and DMA
channels) to hardware components, but that's about as far as the BIOS'
influence on the running system goes.
The decision is actually a very pragmatic one: The BIOS has always been
designed to run in 16-bit Real Mode, and invoking it from 32-bit
protected mode (not to mention 64-bit long mode) would add a shitload of
overhead. The 16-bit mode also limits the efficiency of the driver code
itself.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On 02/06/2016 09:49 AM, dick balaska wrote:
> Am 2016-06-02 01:22, also sprach clipka:
>
>> No; the BIOS uses the same principle, but different interrupts.
>>
>> BIOS-level disk I/O, for instance, uses INT 13h.
>
> I haven't been inside of Windows since ... XP. Do they *still* use that?
> I recall an early Linux decision was to get the hell away from the BIOS
> asap and stay away. -- Device drivers uber alles.
AFAIK, both Windows and Linux use the BIOS to figure out your hardware
layout early in the boot sequence... and then never touch it ever again.
Basically, once the OS kernel has figured out your memory map and which
regions to not clobber, it switches to its own native device drivers,
exits real mode and enters protected mode, and never looks back.
[U]EFI changes this, in that it allows a protected-mode OS to directly
request services from it. Even so, nobody really uses this except for
boot configuration tasks. (If only because your OS still needs to work
on old BIOS systems as well...)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|
 |