Recent

Author Topic: Reimplementing TObject  (Read 12622 times)

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #15 on: August 25, 2021, 08:25:30 am »
But i cannot undestand why benibela option doesn't work...
This is likely because the code is put into ".text" section of the executable.

Such code is marked as "READONLY", meaning when the executable is loaded (and the process is started) the memory area is marked as readonly and cannot be modified.
This quite a handy feature as prevents the code from being modified by accident (i.e. due to some bad code).
Otherwise some errors might turn debugging into hell, since they would be modifying the existing code.

An attempt to modify VMT is the exact example of an attempt to change READONLY memory.


The attribute of readonly is assigned by the linker that's generating the executble. I don't think that the FPC internal linker allows to use a different attribute. But using the external linker (ld) it might be possible


Oh, i hate these things. I hate when a compiler thinks that programmers are stupid or we don't know what we are doing.
is there any way (compiler/linker option) to avoid this?
On the other hand, i could recompile RTL, but a simple way like to add a "uses" for a debug unit without need of recompilation was far way better.

440bx

  • Hero Member
  • *****
  • Posts: 5805
Re: Reimplementing TObject
« Reply #16 on: August 25, 2021, 08:42:56 am »
is there any way (compiler/linker option) to avoid this?
It's quite likely that, at least under Windows, you can change the code segment's protection to make it writeable.  (if memory serves, VirtualProtect does the trick.)

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #17 on: August 25, 2021, 10:21:56 am »
Ok, i Will try, but i would prefer some compiler/linker option. It should be a programmers choice, not a compiler choice, and even less a OS option

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Reimplementing TObject
« Reply #18 on: August 25, 2021, 04:55:22 pm »
Ok, i Will try, but i would prefer some compiler/linker option. It should be a programmers choice, not a compiler choice, and even less a OS option
Technically it's the developer's choice.
Assuming that a developer knows on how to use the linker and instruct the linker to mark the code section as writable.

But if the developer doesn't specify the code section as writable or not, linker has to apply some defaults to it. And default is Read-only (and Execute).

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #19 on: August 25, 2021, 08:24:10 pm »
Agree skalogryz, but i do not know how to instruct the linker to mark the section as writable. Someone has done this before?
I have read (not deeply just now) and i do not find the way i could do that.

On the other hand i am reading "classes" too.

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Reimplementing TObject
« Reply #20 on: August 25, 2021, 08:37:22 pm »
Hi!

from man ld:

--omagic
    Set the text and data sections to be readable and writable. Also, do not page-align the data segment, and disable linking against shared libraries. If the output format supports Unix style magic numbers, mark the output as "OMAGIC". Note: Although a writable text section is allowed for PE-COFF targets, it does not conform to the format specification published by Microsoft.

Winni

MarkMLl

  • Hero Member
  • *****
  • Posts: 8504
Re: Reimplementing TObject
« Reply #21 on: August 25, 2021, 09:01:06 pm »
Hi!

from man ld:

--omagic
    Set the text and data sections to be readable and writable. Also, do not page-align the data segment, and disable linking against shared libraries. If the output format supports Unix style magic numbers, mark the output as "OMAGIC". Note: Although a writable text section is allowed for PE-COFF targets, it does not conform to the format specification published by Microsoft.

Winni

In which case it should be possible to do it from the project's Project options -> Compilation and linkage -> Pass options to linker. I use that for --build-id, can't remember whether I've tried anything else.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #22 on: August 26, 2021, 12:07:02 am »
Well. I tried it manually (windows 10):

D:\Programacion\Pascal\Librerias\Dani\debug>C:\lazarus\fpc\3.2.0\bin\x86_64-win64\fpc.exe  -MObjFPC -omagic -Scghi -O1 -g -gl -l -vewnhibq -Filib\x86_64-win64 -Fu. -FUlib\x86_64-win64 -FE. -oProject1.exe project1.pas

(code is benibela showed)

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode objfpc}{$H+}
  3. uses classes;
  4.  
  5. procedure MyAfterConstruction(self: TObject);
  6. begin
  7.    writeln('AfterConstruction: ', self.ClassName);
  8. end;
  9.  
  10. var o: TObject;
  11.   ovmt: PVmt;
  12.   p: TPersistent;
  13.   l: TList;
  14. begin
  15.   ovmt:=PVmt(TObject);
  16.   ovmt^.vAfterConstruction := @MyAfterConstruction;
  17.   ovmt:=PVmt(TPersistent);
  18.   ovmt^.vAfterConstruction := @MyAfterConstruction;
  19.   ovmt:=PVmt(TList);
  20.   ovmt^.vAfterConstruction := @MyAfterConstruction;
  21.   o := TObject.create;
  22.   p := TPersistent.Create;
  23.   l := TList.Create;
  24. end.          

I tried with GUI in project options, (but inserts a -k), and same thing.
Any help to make this working?

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Reimplementing TObject
« Reply #23 on: August 26, 2021, 01:04:14 am »
I tried with GUI in project options, (but inserts a -k), and same thing.
this is how it supposed to be, with -k.

D:\Programacion\Pascal\Librerias\Dani\debug>C:\lazarus\fpc\3.2.0\bin\x86_64-win64\fpc.exe  -MObjFPC -Xe -k--omagic -Scghi -O1 -g -gl -l -vewnhibq -Filib\x86_64-win64 -Fu. -FUlib\x86_64-win64 -FE. -oProject1.exe project1.pas

"-Xe" - is forcing FPC to use the external linker (ld)
"--k--omagic" - makes FPC to pass "--omagic" to the external linker. FPC internal linker doesn't recognize the parameter, so -Xe and the external linker have to be used.

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #24 on: August 26, 2021, 02:11:55 am »
well. it made a huge .exe 3,7Mb, and... "The application could not be started correctly (0xc00000005).

skalogryz, how did you get it to work? I'm feeling stupid.. A simple thing like telling to linker that the .text area of the code is read-write and not read only is like being too tricky. And even i don't agree about why it should even not be read-write by default, it should by reverse: allow protected memory zones by devs choice If platform supports that.

And even i feel that i cannot do it by directives on .pas file like others.

So the simplest way i tried to debug (simply adding a .debug unit with initialization) which is not invasive, should be more complicated for the user.
It seems that (at least in windows) could be simply doing it by code calling win api virtualprotect and then turn off these directions. But i feel like breaking more a thing that i think it's not broken, but forces me to broke it.


skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Reimplementing TObject
« Reply #25 on: August 26, 2021, 02:45:31 am »
skalogryz, how did you get it to work?
I didn't. I didn't even try to make it work. I'm just explaining why it might not work.

Big executable is due to " -g -gl" - debugging information

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #26 on: August 26, 2021, 02:55:02 am »
Well, i can't do it work. I will try tomorrow with a cleaner mind  :D

On the other hand (about helpers and this case) and for the teoretical view...
why a helper cannot substitute a method? what's the sense of this? (or at least bridging it).
I think that if a helper is allowed (and really for me a helper is for doing some dirty things because we don't/can't recompile former unit or is not practical), why are the helpers having so little power? If a programmer need to go dirty, then give the full tool, not a partial one.

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Reimplementing TObject
« Reply #27 on: August 26, 2021, 04:56:12 am »
why a helper cannot substitute a method? what's the sense of this? (or at least bridging it).
Helpers do not replace or override the methods, instead they just allow to add additional methods.

Helpers are useful to keep the name space clean (which is a questionable feature within Pascal units)
AND are useful to bring some compatibility. (i.e. if a method was removed from a newer version of the library. So instead of rewriting the entire code, helpers can be utilized to restore the functionality)

Overall what you're requesting is "prototype-based" development language (i.e. Javascript). Pascal is not one of them.
« Last Edit: August 26, 2021, 04:58:36 am by skalogryz »

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #28 on: August 26, 2021, 11:18:16 am »
Quote
Helpers do not replace or override the methods, instead they just allow to add additional methods.
Why not? It has no sense for me.

Quote
Helpers are useful to keep the name space clean (which is a questionable feature within Pascal units)
I don't think it should be a feature. But if wanted that a languaje syntax would help diferentiation a simple "substitute" or "reimplements"  keyword, like "reintroduce" would be ok. It's only to change/redirect the pointers in VMT. Even more simple and even space names cleaner

Quote
AND are useful to bring some compatibility. (i.e. if a method was removed from a newer version of the library. So instead of rewriting the entire code, helpers can be utilized to restore the functionality)
I do not agree, a virtual method cannot be added, for example. And like if a method is removed, same for a method  that has some undesired behaviour and you do not want to make the code.

Quote
Overall what you're requesting is "prototype-based" development language (i.e. Javascript). Pascal is not one of them.

I think i have explained really bad. I hate prototype and i like low-level. For me, Pascal is same low level as C. In pascal, i watn make my own memory manager and trace pointers (i think it's a good low level feature). But with a high level structure i cannot do it. And i love have in my coding way a Debug unit that if used or activated by a compiler optional can do bookeeping because i will do some low-dirty-or desirable to trace for the sanity of the code and not propagate memory holes and make a more readable dumps. Not a javascrip thing. :D. I think i'm requesting low level control of facilities, not prototype ones.

The way here is that TObject has a AfterConstruction method virtual y with empty code called by the magic of the compiler after doing a Create thing.
And even TObject doesn't have a simple feature like setting your own method to be called AfterConstruction. So you have two bad things. A Grandgrandfather that imposes no using this feature for what it's needed because in all programs we need to descend from TObject. And it was a very EASY feature. You can do this for your own descendant overriding it, but you cannot use in were it would be really practical, in the tons of code that are using that. And then we use mosters like heaptrc to debug high level contruct instead of high-level aproach.

For example a method like setAfterConstructionMethod (const MyNewMethod:TMethod) in TObject would be very handy. Same for BeforeDestruction. Writen like is now it has no sense. Internally in Afterconstruction if a method is set, then call it, if not, proceed normally. It's backward compatible too as an added feature.
Same that helpers or some tools could "reemplace" not "reintroduce" a method could be a way. Give the low-tool to the programmer. If devs thinks that programmer are doing daring thing, enforce with compilation options or syntax, but do not put programmers hands out of trying things








« Last Edit: August 26, 2021, 11:33:49 am by damieiro »

PascalDragon

  • Hero Member
  • *****
  • Posts: 6184
  • Compiler Developer
Re: Reimplementing TObject
« Reply #29 on: August 26, 2021, 06:15:14 pm »
BeniBela: I would love that redirection would be done before any execution of other units (i would put that redirection in a "debug unit", so that "initialization" should be the first one. Is there a way or to force it some how? (for example, being the first in the uses clause on main program). <Edited: I see documentations and doubts in forum that it should be the first unit and not have nested units with initilization or will be the more nested initialization first).

For such tricks it should indeed be one of the first units and should not use other units that have initialization sections that instantiate objects. Please note that you'll have to pull that trick for each and every class separately, because each class has a full copy of the VMT.

Oh, i hate these things. I hate when a compiler thinks that programmers are stupid or we don't know what we are doing.

The main purpose of this is to avoid accidental mistakes. 99.99% of developers don't ever need to modify the VMT thus it makes sense to keep it protected just like any other readonly data. On ELF (e.g. Linux) and Mach-O (macOS) based targets that is simply not the case due to some problems with relocations in these files for readonly sections otherwise it would be blocked there as well.

is there any way (compiler/linker option) to avoid this?

No documented one. Change the protection at runtime if you need it, e.g. using VirtualProtect on Windows as 440bx suggested.

Quote
Helpers do not replace or override the methods, instead they just allow to add additional methods.
Why not? It has no sense for me.

Because that is simply not the purpose of a helper. They don't modify the existing type, they add to it at compile time. It is essentially the Object Pascal variant of extension methods in C# and that's where they originally came from (Delphi.NET introduced them originally).

I think i have explained really bad. I hate prototype and i like low-level. For me, Pascal is same low level as C. In pascal, i watn make my own memory manager and trace pointers (i think it's a good low level feature). But with a high level structure i cannot do it. And i love have in my coding way a Debug unit that if used or activated by a compiler optional can do bookeeping because i will do some low-dirty-or desirable to trace for the sanity of the code and not propagate memory holes and make a more readable dumps. Not a javascrip thing. :D. I think i'm requesting low level control of facilities, not prototype ones.

Pascal might be as low level as C, but it simply is not C. The language simply introduced some concepts that protects the programmer from common mistakes in C and in addition to that classes are not something that is part of C. So expecting class metatypes or virtual methods of already declared classes to be easily changeable is simply wrong from a language point.

 

TinyPortal © 2005-2018