GCC question

H

Herbert Kleebauer

As part of a simple X demo for Linux I send the byte string
in "send8" to the X server. This byte string displays a circle
at the x/y position send8.s8x/send8.s8y. In "display()" a 8x8
grid of circles is displayed. But when I compile the code
below with -O3, then in the "j" loop not send8.s8x is incremented
by 50 (send8.s8x += 50;) but only a local copy of it. As a result,
the byte string sent to the X server (x_send((char*)&send8, sizeof(send8))
contains always the same x position for the circle. If I replace
"struct .." by "volatile struct .." all is ok. Is this a correct
behaviour of the compiler? Do I have to declare any global variable
"volatile" if I use only the address of the variable in a function
call?




struct {char a[2]; short b; int s8a,s8b; short s8x,s8y,c[4];}
send8={71,0,sizeof(send8)/4,0,0,0,0,50,50,0*64,360*64};

int eax, x_handle, brett[10][10];

void display();
void x_send();
void exit();

main()
{display();}


void display()
{int i,j;
send8.s8y=0;
for (i=1;i<9;i++)
{send8.s8x=0;
for (j=1;j<9;j++)
{if (brett[j]) x_send((char*)&send8, sizeof(send8));
send8.s8x += 50;
}
send8.s8y += 50;
}
}

/******************** send message to X server ****************************/
void x_send(p,n) char *p; int n;
{while (n)
{eax=-11;
const int par[]={x_handle, (int)p, n, 0};
while (eax == -11)
asm volatile ("int $0x80":"=a" (eax): "0" (102), "b" (9), "c" (&par));
if (eax>=-4095) exit(0);
n -= eax; p += eax;
}
}
/**************************************************************************/


display:
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %esi
pushl %ebx
subl $44, %esp
movl x_handle, %eax
movw $0, send8+14
leal -28(%ebp), %ecx
movl $1, -48(%ebp)
movl $40, -52(%ebp)
movl %eax, -40(%ebp)
..L17:
movl -52(%ebp), %eax
movw $0, send8+12 <- send8.s8x
movl $1, -44(%ebp)
movw $0, -34(%ebp) <- local copy of send8.s8x
addl $brett, %eax
movl %eax, -32(%ebp)
..L18:
movl -32(%ebp), %eax
movl 4(%eax), %eax
testl %eax, %eax
je .L19
movl $send8, %edi
movl $24, %esi
movl $102, %edx
movl $9, %ebx
.p2align 4,,7
..L21:
movl -40(%ebp), %eax
movl $-11, eax
movl %edi, -24(%ebp)
movl %esi, -20(%ebp)
movl %eax, -28(%ebp)
movl $0, -16(%ebp)
.p2align 4,,7
..L22:
movl %edx, %eax
#APP
int $0x80
#NO_APP
cmpl $-11, %eax
je .L22
cmpl $-4095, %eax
movl %eax, eax
jge .L37
subl %eax, %esi
je .L19
addl %eax, %edi
jmp .L21
..L19:
addl $1, -44(%ebp)
addw $50, -34(%ebp) <- only the local copy is modified but not send8.s8x
addl $4, -32(%ebp)
cmpl $9, -44(%ebp)
jne .L18
addl $1, -48(%ebp)
addw $50, send8+14
addl $40, -52(%ebp)
cmpl $9, -48(%ebp)
movw $400, send8+12
jne .L17
addl $44, %esp
popl %ebx
popl %esi
popl %edi
popl %ebp
ret
 
W

Walter Roberson

As part of a simple X demo for Linux I send the byte string
in "send8" to the X server. This byte string displays a circle
at the x/y position send8.s8x/send8.s8y. In "display()" a 8x8
grid of circles is displayed. But when I compile the code
below with -O3, then in the "j" loop not send8.s8x is incremented
by 50 (send8.s8x += 50;) but only a local copy of it. As a result,
the byte string sent to the X server (x_send((char*)&send8, sizeof(send8))
contains always the same x position for the circle. If I replace
"struct .." by "volatile struct .." all is ok. Is this a correct
behaviour of the compiler?
void x_send(p,n) char *p; int n;
{while (n)
{eax=-11;
const int par[]={x_handle, (int)p, n, 0};
while (eax == -11)
asm volatile ("int $0x80":"=a" (eax): "0" (102), "b" (9), "c" (&par));
if (eax>=-4095) exit(0);
n -= eax; p += eax;
}
}

Here is what the C standard has to say on the topic:

J.5 Common extensions

The following extensions are widely used in many systems, but are
not portable to all implementations. The inclusion of any
extension that may cause a strictly conforming program to become
invalid renders an implementation nonconforming. Examples of such
extensions are new keywords, extra library functions declared in
standard headers, or predefined macros with names that do not
begin with an underscore.
[...]
J.5.10 The asm keyword

The asm keyword may be used to insert assembly language directly
into the translator output (6.8). The most common implementation
is via a statement of the form:
asm ( character-string-literal );


Decoded from standard-speak to human-speak, what this means is:

When you include an "asm" keyword in a program, the implementation
is allowed to do almost anything with it. -Every- behaviour of
the "asm" keyword is correct as far as the C standard cares.
Sometimes, by coincidence or bad luck, the compiler might do
something apparently meaningful with the "asm" keyword, but
the standard would be completely happy if the implementation
treated the "asm" keyword as meaning "locate all files that the
user has write access to and delete them or scribble garbage in them."


Your particular implementation might make more pleasant contracts
with you as to what "asm" means to it. Perhaps. The C standard won't
care if the contract (Application Programmer Interface, API)
documentation is a pack of lies through and through, but if you
care to be entertained by reading the sweet lies, you should
check the documentation specific to your implementation, as the
above is ALL that the C standard has to say about "asm".
 
K

Keith Thompson

Herbert Kleebauer said:
As part of a simple X demo for Linux I send the byte string
in "send8" to the X server. This byte string displays a circle
at the x/y position send8.s8x/send8.s8y. In "display()" a 8x8
grid of circles is displayed. But when I compile the code
below with -O3, then in the "j" loop not send8.s8x is incremented
by 50 (send8.s8x += 50;) but only a local copy of it. As a result,
the byte string sent to the X server (x_send((char*)&send8, sizeof(send8))
contains always the same x position for the circle. If I replace
"struct .." by "volatile struct .." all is ok. Is this a correct
behaviour of the compiler? Do I have to declare any global variable
"volatile" if I use only the address of the variable in a function
call?

struct {char a[2]; short b; int s8a,s8b; short s8x,s8y,c[4];}
send8={71,0,sizeof(send8)/4,0,0,0,0,50,50,0*64,360*64};

int eax, x_handle, brett[10][10];

void display();
void x_send();
void exit();

main()
{display();}


void display()
{int i,j;
send8.s8y=0;
for (i=1;i<9;i++)
{send8.s8x=0;
for (j=1;j<9;j++)
{if (brett[j]) x_send((char*)&send8, sizeof(send8));
send8.s8x += 50;
}
send8.s8y += 50;
}
}

/******************** send message to X server ****************************/
void x_send(p,n) char *p; int n;
{while (n)
{eax=-11;
const int par[]={x_handle, (int)p, n, 0};
while (eax == -11)
asm volatile ("int $0x80":"=a" (eax): "0" (102), "b" (9), "c" (&par));
if (eax>=-4095) exit(0);
n -= eax; p += eax;
}
}
/**************************************************************************/

[assembly code snipped]

You shouldn't *normally* have to mark a variable as volatile if you
use its address in a function call. The fact that you take its
address should be sufficient to force the compiler to (generate code
to) keep the object's value at its assigned address, at least whenever
it might be accessed via that address.

But your use of an asm statement, which is a gcc-specific extension,
means that, as far as the C standard is concerned, all bets are off.
You're doing some other low-level stuff here that might also confuse
things.

I suggest asking in gnu.gcc.help.

A few comments on your code, not relevant to your actual question:

Rather than declaring exit() yourself, you should add
"#include <stdlib.h>".

Your code layout is far too compact for my taste.

The main function should be declared "int main(void)". You can
probably get away with just "main()", but there's no cost in getting
it right.

You use an old-style declaration for x_send. I can think of no good
reason for doing that. Rather than
void x_send(p,n) char *p; int n;
write
void x_send(char *p, int n)

Note that you call it with a second argument of type size_t, where it
expects an int. Using a prototype causes the argument to be converted
to the correct type. Without a prototype, the calling code assumes
that the function expects a size_t argument, and the function assumes
that it was passed an int; it might "work" if int and size_t happen to
be the same size.

I think there are other problems in your code, but this is all I have
time for now.
 
J

Johannes Bauer

Herbert said:
asm volatile ("int $0x80":"=a" (eax): "0" (102), "b" (9), "c" (&par));

Have you considered the possibility that the implicit function call
which is made by the software interrupt (trap) clobbers some registers,
mainly the volatile ones which you fail to declare for the compiler?
When the compiler does not assume these registers are changed by your
call, some optimization may break your code. Try to insert the proper
clobber registers and see if it works.

Kind regards,
Johannes
 
A

Antoninus Twink

But when I compile the code below with -O3, then in the "j" loop not
send8.s8x is incremented by 50 (send8.s8x += 50;) but only a local
copy of it. As a result, the byte string sent to the X server
(x_send((char*)&send8, sizeof(send8)) contains always the same x
position for the circle. If I replace "struct .." by "volatile struct
.." all is ok. Is this a correct behaviour of the compiler? Do I have
to declare any global variable "volatile" if I use only the address of
the variable in a function call?

As a general rule, if you use or modify the value of a variable
somewhere where the C compiler can't see that - for example, if it's
memory mapped from hardware or (as in your case) via inline assembly -
then yes, you need to qualify it as volatile to make sure that your C
compiler doesn't optimize away reads or writes to the variable, store it
in a register, or perform other trickery.
 
J

jacob navia

Johannes said:
Have you considered the possibility that the implicit function call
which is made by the software interrupt (trap) clobbers some registers,
mainly the volatile ones which you fail to declare for the compiler?
When the compiler does not assume these registers are changed by your
call, some optimization may break your code. Try to insert the proper
clobber registers and see if it works.

Kind regards,
Johannes

It would be better if the assembly code were in an external
function. There wouldn't be any problems of register clobbering
in that case...
 
K

Kaz Kylheku

As part of a simple X demo for Linux ...

.... you couldn't refrain from using inline assembly language to code an
operating system call.
/******************** send message to X server ****************************/
void x_send(p,n) char *p; int n;
{while (n)
{eax=-11;
const int par[]={x_handle, (int)p, n, 0};
while (eax == -11)
asm volatile ("int $0x80":"=a" (eax): "0" (102), "b" (9), "c" (&par));
if (eax>=-4095) exit(0);
n -= eax; p += eax;
}
}

Even when there is a genuine need to code a system call sequence (because the
library doesn't provide a wrapper for the desired system call) it's usually
done using the syscall function, not with inline assembly.

Come back when you're out of programming puberty.
 
H

Herbert Kleebauer

Kaz said:
On 2008-07-28, Herbert Kleebauer <[email protected]> wrote:
Even when there is a genuine need to code a system call sequence (because the
library doesn't provide a wrapper for the desired system call) it's usually
done using the syscall function, not with inline assembly.

Come back when you're out of programming puberty.

Very funny. C is a language which, when processed by the compiler,
generates machine code, but C isn't a religion. I have to rely on
the correctness of the compiler, but why should I also rely on
the correctness of wrappers and include files?

This X demo was original written in assembly. Each byte in the
executable is explicitly specified in the source code. Not even
a single byte is added from a library or added by a linker. The code
also doesn't use any dynamic library, only the native int80 Linux
interface is used. And the C version also should not use anything
which is not directly specified in the source (at least after the
start-up code calls main() and until main() returns).
 
H

Herbert Kleebauer

Keith said:
Herbert Kleebauer <[email protected]> writes:

A few comments on your code, not relevant to your actual question:

Rather than declaring exit() yourself, you should add
"#include <stdlib.h>".

Why hide thing in an include file? It makes the source much more clear
if you copy&paste all _needed_ definitions into the main source.
Your code layout is far too compact for my taste.

The main function should be declared "int main(void)". You can
probably get away with just "main()", but there's no cost in getting
it right.

int is the default and where is the difference between no parameter
and void as parameter?

You use an old-style declaration for x_send. I can think of no good
reason for doing that. Rather than

And where is the advantage of the new-style declaration? The old
one immediately shows the order of the used parameters by separating the
type declaration whereas the new one is much to confusing.
void x_send(p,n) char *p; int n;
write
void x_send(char *p, int n)

Note that you call it with a second argument of type size_t, where it
expects an int. Using a prototype causes the argument to be converted
to the correct type. Without a prototype, the calling code assumes
that the function expects a size_t argument, and the function assumes
that it was passed an int; it might "work" if int and size_t happen to
be the same size.

This is code for a 32 bit x86 Linux and if the compiler uses anything
else than a 32 bit word for the size of a variable then I suppose I
don't want to use that compiler at all (never used "size_t").

I think there are other problems in your code,

Seems so. Never trust a compiler, always check the generated code.
 
H

Herbert Kleebauer

Antoninus said:
On 28 Jul 2008 at 18:44, Herbert Kleebauer wrote:

As a general rule, if you use or modify the value of a variable
somewhere where the C compiler can't see that - for example, if it's
memory mapped from hardware or (as in your case) via inline assembly -
then yes, you need to qualify it as volatile to make sure that your C
compiler doesn't optimize away reads or writes to the variable, store it
in a register, or perform other trickery.

But I still think this is an illogical behaviour of GCC even if it,
as pointed out in other replies, doesn't violate the C specification.


The compiler generates for the variable initialization "send8.s8x=0;"
the corresponding assembly code "movw $0, send8+12", but then within the
loop it uses a local variable instead (which also is initialized to 0):

movw $0, -34(%ebp)

and which then is incremented by 50 within the loop (instead of
the global variable):

addw $50, -34(%ebp)

But this local variable -34(%ebp) is a write only variable, it's
content is never used, even after the loop it's content isn't
written back to the global variable. Because already at compile
time gcc knows that the loop is executed 8 times and in each
iteration the variable is incremented by 50, gcc doesn't need
the value in the local variable but can directly store 400 in
the global variable:

movw $400, send8+12

What sense does it make to use a write only, temporary local
variable instead of the global variable?
 
B

Ben Bacarisse

Herbert Kleebauer said:
Very funny. C is a language which, when processed by the compiler,
generates machine code, but C isn't a religion. I have to rely on
the correctness of the compiler,

.... and the compiler has to reply on the correctness of your code.
You have done almost everything possible to prevent the compiler from
doing what you want it to. The compiler can prove that x_send is
never called, so I am not surprised you get unexpected results. This
may not be what is happening, but until you write C that correctly
expresses your intent, all bets are off.
 
S

santosh

Herbert said:
Why hide thing in an include file? It makes the source much more clear
if you copy&paste all _needed_ definitions into the main source.

Herbert, your philosophy of C programming will clash most violently with
the consensus here. Be warned in advance. :)

The problems with the copy and paste method are many. What if the
declaration in itself depends on more declarations or definitions? What
if the compiler recognises Standard headers and activates
special "magic" for processing them? How do you propose to keep all the
declarations perfectly in sync with each other across, say, a hundred
source files program utilising all the Standard C headers? What will
you do if you change compilers and the new one chokes on the
declarations from the old one's headers?

In short, why repeat all the hard work that your compiler vendor has
already gone through?
int is the default and where is the difference between no parameter
and void as parameter?

There is. If you supply void, you declare a full *prototype* , which
enables the compiler to cross-check declaration, definition and use. If
you leave parenthesis empty, you are using the so-called "old style"
declaration, which turns off automatic checking of calls, conversions
etc.
And where is the advantage of the new-style declaration? The old
one immediately shows the order of the used parameters by separating
the type declaration whereas the new one is much to confusing.

The main advantage lies in increased verification done by the compiler.
But I wouldn't expect you to agree with me.
This is code for a 32 bit x86 Linux and if the compiler uses anything
else than a 32 bit word for the size of a variable then I suppose I
don't want to use that compiler at all (never used "size_t").



Seems so. Never trust a compiler, always check the generated code.

Shh. Not here! :)
 
K

Kenneth Brody

Herbert said:
Keith Thompson wrote: [...]
Rather than declaring exit() yourself, you should add
"#include <stdlib.h>".

Why hide thing in an include file? It makes the source much more clear
if you copy&paste all _needed_ definitions into the main source.
[...]

You're kidding, right? Oh, you're serious?!?

I suppose you also have this in many of your source files:

extern int errno;

I can give you several examples where this will fail miserably,
because errno is really a macro to a thread-aware function which
returns the thread's errno.

I suppose you also copy-and-paste structure definitions, as well?
How far do you take it? Do you have any #include's at all?

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
K

Kenny McCormack

Very funny. C is a language which, when processed by the compiler,
generates machine code, but C isn't a religion.

Here, it is. I have made this point many times. The correspondence
between the sorts of (ridiculous) beliefs held by religious people and
the sorts of ridiculous beliefs held by the CLC regulars is well beyond
the level of statistical noise.

It should, therefore, come as no surprise to you, to find out that many
of the CLC regulars are, in fact, religious whackos in real life.

Voltaire said it best (this applies equally well to the silliness
practiced by organized religion and the silliness practiced in this
newsgroup):

Those who can make you believe absurdities can make you commit
atrocities.

Voltaire
 
K

Kenny McCormack

Herbert said:
Keith Thompson wrote: [...]
Rather than declaring exit() yourself, you should add
"#include <stdlib.h>".

Why hide thing in an include file? It makes the source much more clear
if you copy&paste all _needed_ definitions into the main source.
[...]

You're kidding, right? Oh, you're serious?!?

I suppose you also have this in many of your source files:

extern int errno;

I can give you several examples where this will fail miserably,
because errno is really a macro to a thread-aware function which
returns the thread's errno.

But of course, all of those examples are OT here, so, in the accepted
dogma of the NG, they don't exist.

So, you fail.
 
K

Keith Thompson

Herbert Kleebauer said:
Why hide thing in an include file? It makes the source much more clear
if you copy&paste all _needed_ definitions into the main source.

For one thing, it means that the declaration will be *correct*.

For another, the implementation is free to implement most library
functions as macros, which might be more efficient. By using your own
declaration, even if you happen to get it right, you prevent the
compiler from generating better code.
int is the default and where is the difference between no parameter
and void as parameter?

int is the default in C90. The "implicit int" feature has been
dropped in the newer C99 standard.
And where is the advantage of the new-style declaration? The old
one immediately shows the order of the used parameters by separating the
type declaration whereas the new one is much to confusing.


This is code for a 32 bit x86 Linux and if the compiler uses anything
else than a 32 bit word for the size of a variable then I suppose I
don't want to use that compiler at all (never used "size_t").

Yes, you certainly did you size_t; that's the result of the sizeof
operator.
Seems so. Never trust a compiler, always check the generated code.

I meant in your source code, not in the generated code.

If you're unwilling to trust any compiler, perhaps you'd be happier
programming in assembly language.
 
W

Walter Roberson

Kenneth Brody said:
I suppose you also have this in many of your source files:
extern int errno;
I can give you several examples where this will fail miserably,
because errno is really a macro to a thread-aware function which
returns the thread's errno.

C89 4.1.3 Errors <errno.h>

errno

which expands to a modifiable lvalue that has type int, the value
of which is set to a positive error number by several library
functions. It is unspecified whether errno is a macro or an
identifier declared with external linkage. If a macro definition
is suppressed in order to access an actual object, or a program
defines an identifier with the name errno, the behavior is undefined.


Thus it is not allowed in C89 for errno to be a macro to a thread-aware
function, because functions can never return lvalues and errno must
expand to a modifiable lvalue.
 
H

Herbert Kleebauer

santosh said:
Herbert Kleebauer wrote:

Herbert, your philosophy of C programming will clash most violently with
the consensus here. Be warned in advance. :)

You moved to c.l.c? Then I doubt the moderator will ever let you come back
to c.l.a.x.

The problems with the copy and paste method are many. What if the
declaration in itself depends on more declarations or definitions?

How else would you learn to understand all the dependencies? Try to
understand a definition and if you have understood it, copy it
into your own source. Then let the compiler check that you didn't
forget any nested definition. This isn't practicable for
producing applications, but it surely isn't a bad way to learn
to understand how things work. And before you start to write
applications you really should know how things work.

In short, why repeat all the hard work that your compiler vendor has
already gone through?

People have learned thousand years ago how to multiply numbers. Why
should every pupil do "all the hard work again" and also learn how
to multiply numbers? Some vendor has put "all the hard work" into
a pocket calculator and all you have to do is to press a few keys,
no need to learn how to multiply. Yes, if i want to know the result
of 12346*98765 I use a pocket calculator, but everybody who uses a
pocket calculator first should learn to multiply numbers so he
understands what happens in the pocket calculator and that he is
able to decide whether the displayed result is in the correct
range at all.

There is. If you supply void, you declare a full *prototype* , which
enables the compiler to cross-check declaration, definition and use. If
you leave parenthesis empty, you are using the so-called "old style"
declaration, which turns off automatic checking of calls, conversions
etc.

I think the comment was not about a prototype definition of main(),
but the main() function itself. And if there are no parameters
between the (), then there is also no need for any conversion.

The main advantage lies in increased verification done by the compiler.
But I wouldn't expect you to agree with me.

Sorry, but I don't understand where in the second version is any more
information which allows the compiler to do any "increased verification".
It's all about readability and I really prefer the first one.


Shh. Not here! :)

As a user of a pocket calculator should be able to do the multiplication
himself, a C programmer should be able to understand the generated
assembly code. And if a program doesn't behaviour as supposed, then
reading the generated assembly code is sometimes the only way to find
the bug (funny things happen when the optimizer rearranges your inline
assembly code so the initialization of registers is moved behind the
assembly instruction which uses the register.


PS:

For the socket connection to the X server I have to send a data packet
consiting of a short and 17 characters. What is the correct way to
declare such a data structure in C (I need a pointer to this
structure and it's size) without having to count the number
of characters by hand.

struct {short a; char b[*];}
a={1,{'/','t','m','p','/','.','X','1','1','-','u','n','i','x','/','X','0'}}

gives: warning: GCC does not yet properly implement '[*]' array declarators
The data is generated correctly but sizeof(a) is 2.


struct {short a; char b[];}
b={1,{'/','t','m','p','/','.','X','1','1','-','u','n','i','x','/','X','0'}}

no warning
The data is generated correctly but sizeof(b) is 2.


struct {short a; char b[17];}
b={1,{'/','t','m','p','/','.','X','1','1','-','u','n','i','x','/','X','0'}}

The generated data is padded with a zero and therefore sizeof(c) is 20 instead
of 19.

So, non of these three versions is really useful.
 
H

Herbert Kleebauer

Ben said:
... and the compiler has to reply on the correctness of your code.
You have done almost everything possible to prevent the compiler from
doing what you want it to.

I mostly don't write programs to get an executable but to learn
something. And you learn most, when you make errors and then
understand why you made this error and why it was an error at all.
I also would like to learn something from your comment, but without
a further explanation I doubt this will be possible.

The compiler can prove that x_send is
never called, so I am not surprised you get unexpected results.

Why do you think x_send is never called?

send8.s8y=0;
for (i=1;i<9;i++)
{send8.s8x=0;
for (j=1;j<9;j++)
{if (brett[j]) x_send((char*)&send8, sizeof(send8));
send8.s8x += 50;
}
send8.s8y += 50;
}

x_send is called 64 times, and 64 circles are drawn on the screen.
The problem is, that the x position of the circle is stored in
send8.s8x and the compiler generated code never updates this variable
(send8.s8x += 50;) but instead updates a temporary local variable
(which is WRITE_ONLY!!!) and therefore all circles are drawn on
the same x position. On the other side, send8.s8y is properly
updated (here no temporary local variable is used) so on the
screen is a row of 8 circles displayed.


So, how can "The compiler can prove that x_send is never called"?
 
C

Chris Torek

I have to rely on the correctness of the compiler, but why should
I also rely on the correctness of wrappers and include files?

You need not "rely on the correctness of the compiler" either, if
you write the code in assembly instead of a compiled language.
This X demo was original written in assembly.

So there you are -- but why, then, did you rewrite it in GNUC?
(It is now written in GNUC, not C, because it uses a GCC-specific
"asm": it will not compile with various non-GNU compilers even when
run on the same Linux machine.)

This is not meant as a rhetorical question. Why did you rewrite
it? It was, I presume, working just fine. You paid a cost in time
in rewriting it, and you are now paying another cost in that the
rewrite does *not* work. What, if anything, did you *gain* by
rewriting it? What else, if anything, do you hope to gain in the
future? And most importantly:
Each byte in the executable is explicitly specified in the source
code. Not even a single byte is added from a library or added by
a linker. The code also doesn't use any dynamic library, only the
native int80 Linux interface is used. And the C version also should
not use anything which is not directly specified in the source (at
least after the start-up code calls main() and until main() returns).

What would you gain if you gave up this level of control? You know
the *cost* of giving it up, but what gains are possible? Make a
list (possibly sketchy) of things you might hope to get out of the
deal. Weigh this against what you must give up. For instance, if
you rely on "wrappers and include files", you will gain the ability
to move the code to non-x86 architectures (as well as not running
into the problem you already ran into).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top