single function with multiple names
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:single function with multiple names
... or using a text transductor that would discover that you're calling XYZ("fmt", 1 , 2 , 3 , 4 ) and therefore replaces XYZ_MACROsiii ...
If you look at OpenGL, for instance, function names always carry the type of their arguments...
If you look at OpenGL, for instance, function names always carry the type of their arguments...
Re:single function with multiple names
I don't think you can define a recursive macro.Candy wrote:I was considering making the ugliest hack I've ever... making either a recursive define or about 60 or so defines so all functions can be used with it... That'd work...
Was the problem that you have different syscalls that take different numbers of arguments, or that you have syscalls that take a variable number of arguments (ala printf)?
If it's the former, you really ought to have a different define for each syscall in any case, and it ought to be defined it such a way that its arguments are type-checked (assign each parameter to a local variable of the correct type inside the curly braces, if you're allergic to inline functions). Trying to use a single syscall macro for every syscall is a much bigger hack than actually doing the work of properly defining the interface for each syscall. The only non-hackish solution to this problem is to use inline functions, everything else subverts or bypasses (modern) C's standard way of declaring these things, type-checking, etc. As of C99, you really ought not need defines at all. That's what const and inline are for. #define has always been a hack.
If the problem is you have syscalls using varargs, I believe you're going to be forced to use a trampoline in that case.
Re:single function with multiple names
Two of my syscalls (well... two very similar ones) have variable arguments that have to be pushed on the stack before the call. The syscall itself doesn't use them as varargs, but as a memory area for a thread creation (the function is tcall and is similar to calling a function, but in this case making a thread that calls the function and not waiting for it to complete). The point is, the C compiler only has to treat it like a normal function, the rest is done by assembly magic in the kernel.Dreamsmith wrote:I don't think you can define a recursive macro.Candy wrote:I was considering making the ugliest hack I've ever... making either a recursive define or about 60 or so defines so all functions can be used with it... That'd work...
Was the problem that you have different syscalls that take different numbers of arguments, or that you have syscalls that take a variable number of arguments (ala printf)?
If it's the former, you really ought to have a different define for each syscall in any case, and it ought to be defined it such a way that its arguments are type-checked (assign each parameter to a local variable of the correct type inside the curly braces, if you're allergic to inline functions). Trying to use a single syscall macro for every syscall is a much bigger hack than actually doing the work of properly defining the interface for each syscall. The only non-hackish solution to this problem is to use inline functions, everything else subverts or bypasses (modern) C's standard way of declaring these things, type-checking, etc. As of C99, you really ought not need defines at all. That's what const and inline are for. #define has always been a hack.
If the problem is you have syscalls using varargs, I believe you're going to be forced to use a trampoline in that case.
there is no way I'm going to convince the compiler to instead of inserting a call ....... to insert an arbitrary sequence of equal length right?
Re:single function with multiple names
I believe so. If you do find a way, please let us know, I for one would love to be able to get rid of my trampolines.Candy wrote:there is no way I'm going to convince the compiler to instead of inserting a call ....... to insert an arbitrary sequence of equal length right?
You could postprocess the output of GCC and replace the CALLs that way. Or, for a real dirty hack, make all the CALLs be to a function that modifies the caller to what you want at run time.
Re:single function with multiple names
all I can think of atm is a special hack to my dynamic linker/loader so it patches up the entire entry instead of just the relocation portion, but this is VERY unportable and VERY hackish. I'd make it replace each time that function occured with an E8 prefix to a normal syscall with a nop at the end, each time the prefix was E9 it'd be replaced with the syscall with a ret at the end and each other time (also all times when it's in the data section) it'd be replaced with the trampoline version. I think this'd kill a LOT of unnecessary calls.Dreamsmith wrote:I believe so. If you do find a way, please let us know, I for one would love to be able to get rid of my trampolines...Candy wrote:there is no way I'm going to convince the compiler to instead of inserting a call ....... to insert an arbitrary sequence of equal length right?
Of course this could also be put in a preprocessor, to accelerate load times.. might be put in a loader section so it links them once and stores a prelinked binary, might be dynamic linking so it only replaces it upon actual call... but I think the best one is just on load.
How am I going to get the C compiler to just leave those references open? OK, back to the drawing board...
Re:single function with multiple names
Ah, I suggested in an edit of my last post something similar, but if you have a dynamic linker, that'd probably be less hackish than my suggestion of a runtime function that modifies the caller. Actually, I don't think it's be very hackish doing it in the dynamic linker. It's the dynamic linker's job to modify code to connect it to the proper destination. If that destination is a syscall, it should be able to modify it appropriately. I'd consider that less of a hack than using a trampoline function, which is what I do now.Candy wrote:all I can think of atm is a special hack to my dynamic linker/loader so it patches up the entire entry instead of just the relocation portion, but this is VERY unportable and VERY hackish.
As far as portability goes, anything that has to modify processor specific object code is unportable. Anything that has to deal with hardware is unportable. Anything that has to deal with CPU state is unportable. The precise job of an operating system is to deal with the nitty-gritty unportable hardware-specific details of a system so that applications don't have to. Being overly concerned about the portability of operating system code is just going to drive you nuts. There's no such thing as a portable syscall interface, for example. So no matter which way you solve this problem, it's not going to be portable.
I don't mean to suggest portability should never be a concern in OS design. Just that it should be trumped by pretty much any other practical concern.
Re:single function with multiple names
That's true, but the entire concept is not portable to any CPU with constant length ops, since the jump-op is replaced by at least 2 and sometimes 3 ops. Because I can stuff the syscall number in AL it only takes a 1-byte immediate, 1-byte opcode, syscall is 2-byte and that leaves for a 1-byte ret at the end. I might convert the mov/syscall/nop to mov in special encoding with syscall only, where the mov would be a mov(zx) eax, byte 1 in bytecode, since I recall the non-fitting form being a 2-byte opcode. Saves a byte on P4's and similar thingy's, since they only store microcoded instructions.Dreamsmith wrote:Ah, I suggested in an edit of my last post something similar, but if you have a dynamic linker, that'd probably be less hackish than my suggestion of a runtime function that modifies the caller. Actually, I don't think it's be very hackish doing it in the dynamic linker. It's the dynamic linker's job to modify code to connect it to the proper destination. If that destination is a syscall, it should be able to modify it appropriately. I'd consider that less of a hack than using a trampoline function, which is what I do now.Candy wrote:all I can think of atm is a special hack to my dynamic linker/loader so it patches up the entire entry instead of just the relocation portion, but this is VERY unportable and VERY hackish.
As far as portability goes, anything that has to modify processor specific object code is unportable. Anything that has to deal with hardware is unportable. Anything that has to deal with CPU state is unportable. The precise job of an operating system is to deal with the nitty-gritty unportable hardware-specific details of a system so that applications don't have to. Being overly concerned about the portability of operating system code is just going to drive you nuts. There's no such thing as a portable syscall interface, for example. So no matter which way you solve this problem, it's not going to be portable.
I don't mean to suggest portability should never be a concern in OS design. Just that it should be trumped by pretty much any other practical concern.
I think this is going to be the final way I'm going to do it. Functions that are normally inlined, if not inlined dynamically linked to a shared library, and upon load replaced with the alternate code or left as the call. Of course, the first version is only going to call the trampoline, no matter what.
Re:single function with multiple names
On a CPU with constant length ops, you're almost certainly not going to need to replace the CALL op with multiple ops, rather, you'll have wide enough instructions to be able to encode each syscall with it own, unique opcode. The syscall number will be encoded into the syscall operation rather than loaded into EAX. Processors of this type generally have available trap opcodes by the thousands.Candy wrote:That's true, but the entire concept is not portable to any CPU with constant length ops, since the jump-op is replaced by at least 2 and sometimes 3 ops.
Re:single function with multiple names
That doesn't account for jumps instead of calls (E9 is jump), where you have to ret after the syscall, because the syscall doesn't ret but sysret (incompatible stack configuration).Dreamsmith wrote:On a CPU with constant length ops, you're almost certainly not going to need to replace the CALL op with multiple ops, rather, you'll have wide enough instructions to be able to encode each syscall with it own, unique opcode. The syscall number will be encoded into the syscall operation rather than loaded into EAX. Processors of this type generally have available trap opcodes by the thousands.Candy wrote:That's true, but the entire concept is not portable to any CPU with constant length ops, since the jump-op is replaced by at least 2 and sometimes 3 ops.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:single function with multiple names
just throwing my 2 cents here: it is indeed possible to have the linker/compiler leave the system_call links "pending" by forcing 'iterative' linking (-r or -i flag for LD) ... After this, you simply crawl the symbol table, looking for patterns you expect (like MyOS_SysCall_<syscall_name>) and patches calls to that function with the wished code (like mov eax, byte svc_id; syscall), provided that your calling code is lower than 5 bytes ...
Toying with the following "add esp, <cleanup_value>" is discouradged since i've seen many optimizer trying to clean several calls in a row rather than cleaning after each call ...
In clicker, such patch of "open symbols" is performed for creating the 'module' format (instead of keeping an ELF/COFF format), and i rather advocate for having it done by a compiling tool than by the kernel-shipped loader for efficiency of loading.
Toying with the following "add esp, <cleanup_value>" is discouradged since i've seen many optimizer trying to clean several calls in a row rather than cleaning after each call ...
In clicker, such patch of "open symbols" is performed for creating the 'module' format (instead of keeping an ELF/COFF format), and i rather advocate for having it done by a compiling tool than by the kernel-shipped loader for efficiency of loading.
Re:single function with multiple names
Which is exactly not what I want. I want it to leave the link open (such as with dynamic linking) when finished linking the file, plus I want all the other linker errors brought by the final link. This is code meant for users, not a specific clique. It should conform to the default wherever possible.Pype.Clicker wrote: just throwing my 2 cents here: it is indeed possible to have the linker/compiler leave the system_call links "pending" by forcing 'iterative' linking (-r or -i flag for LD) ... After this, you simply crawl the symbol table, looking for patterns you expect (like MyOS_SysCall_<syscall_name>) and patches calls to that function with the wished code (like mov eax, byte svc_id; syscall), provided that your calling code is lower than 5 bytes ...
The caller pushed the values, the caller must also clear them. This isn't pascal. Anyway, the C programs generated do this for all normal calls so it's useable in this way.Toying with the following "add esp, <cleanup_value>" is discouradged since i've seen many optimizer trying to clean several calls in a row rather than cleaning after each call ...
Loading efficiency was hoping to be achieved using prelinking to a certain location and storing a cached executable. This makes the actual binary portable across upgrades etc. but keeps the speed of prelinked stuff, even for things that may be dynamically relocated. It also allows me to change the program virtual base any time I damn well pleaseIn clicker, such patch of "open symbols" is performed for creating the 'module' format (instead of keeping an ELF/COFF format), and i rather advocate for having it done by a compiling tool than by the kernel-shipped loader for efficiency of loading.
Re:single function with multiple names
Why not? It's no easier or more difficult to deal with either case, just keep it straight which case you're dealing with. Note: There are no routines on an Intel processor that must be returned from using an iret, or retf, or any other particular construction -- all that is required is that you return to the right place with the stack in order. The same is true on any other processor. All the various different forms of return instructions are their because it's conveninent. Absolutely none of them are there because they are necessary in some circumstance or other.Candy wrote:That doesn't account for jumps instead of calls (E9 is jump), where you have to ret after the syscall, because the syscall doesn't ret but sysret (incompatible stack configuration).
If you understand that, you can see why your concern above is a non-issue. All your routines can sysret at the end, regardless of how they were called -- you just need to make sure that everything is setup to go to the right place when that happens. That changes how you enter the function, not how you exit it, and since you're patching the entry opcode(s) into the code, that's a trivially easy thing to arrange.
Re:single function with multiple names
SYSCALL defines one (1) entry point systemwide. There is no second alternative route for any other calling interface. There is no space for loading a 16-bit register where I might abuse a bit (they all need a 2-byte opcode + 2-byte immediate, plus the 2-byte syscall > 5byte). The idea of keeping this manageable is to keep it as simple as possible. Only substitute the call / jump things, leave the rest. Anyway, I'm only aiming for X86 stuff, so it's not that important. Yet, it's a consideration.Dreamsmith wrote: Why not? It's no easier or more difficult to deal with either case, just keep it straight which case you're dealing with. Note: There are no routines on an Intel processor that must be returned from using an iret, or retf, or any other particular construction -- all that is required is that you return to the right place with the stack in order. The same is true on any other processor. All the various different forms of return instructions are their because it's conveninent. Absolutely none of them are there because they are necessary in some circumstance or other.
If you understand that, you can see why your concern above is a non-issue. All your routines can sysret at the end, regardless of how they were called -- you just need to make sure that everything is setup to go to the right place when that happens. That changes how you enter the function, not how you exit it, and since you're patching the entry opcode(s) into the code, that's a trivially easy thing to arrange.
Re:single function with multiple names
Not really. It's not a consideration on x86 because it doesn't have constant length operations, and it's not a problem on processors with constant length operations because you generally have a ridiculous number of opcodes to use for traps, and thus would not need to worry about encoding it as multiple operations as you're doing on the x86. This solution would only be not portable to machines with constant length operations but only a single opcode available for syscall trapping. To the best of my knowledge, there is no such processor, at least in the 32-bit or greater era...Candy wrote:Anyway, I'm only aiming for X86 stuff, so it's not that important. Yet, it's a consideration.
Re:single function with multiple names
>Is it possible in C/C++ to create a function with more than one function name?
Code: Select all
typedef unsigned pid_t;
int kill(pid_t pid) { return pid; }
int killp(pid_t pid) __attribute__((weak, alias("kill")));