System::Call '${sysGetDiskFreeSpaceEx}(r0,.,,.r1)'
If i'm right, r0: directoryname, free bytes, number of bytes, number oof free bytes, but what actually means the r0,.,.,r1?
Thx for the help!
${...} is a define so when you want to know how some things work then the first thing you should do is to find out what the define does: !error "${sysGetDiskFreeSpaceEx}" will print !error: kernel32::GetDiskFreeSpaceEx(t, *l, *l, *l) i
In the System readme you will find this nugget:
PARAMS, RETURN and OPTIONS can be repeated many times in one Get/Call
line. When repeating, a lot can be omitted, and only what you wish to
change can be used. Type, source and/or destination can be omitted for
each parameter, even the return value. Options can be added or
removed. This allows you to define function prototypes and save on
some typing.
So ${sysGetDiskFreeSpaceEx} is a prototype that specifies the parameter count and types but it does not specify parameter source and destination.
What is the parameter syntax?
The parameters list is separated by commas. Each parameter is combined
of three values: type, source and destination. Type can be an integer,
a string, etc. Source, which is the source of the parameter value, can
be a NSIS register ($0, $1, $INSTDIR), the NSIS stack, a concrete
value (5, "test", etc.) or nothing (null). Destination, which is the
destination of the parameter value after the call returns, can be a
NSIS register, the NSIS stack or nothing which means no output is
required. Either one of source or destination can also be a dot (`.')
if it is not needed.
We can now expand the entire call !error 'System::Call "${sysGetDiskFreeSpaceEx}(r0,.,,.r1)"' and this gives us !error: System::Call 'kernel32::GetDiskFreeSpaceEx(t, *l, *l, *l) i(r0,.,,.r1)'
If we merge the repeated parameter definitions we get kernel32::GetDiskFreeSpaceEx(tr0, *l., *l, *l.r1)i.
So parameter 1 is a string (LPTSTR on MSDN) with the source r0 (NSIS register $0).
Parameter 2 and 3 have no source and no destination, only parameter 2 uses a . (dot) but the end result is the same; no input and no output. The only important part here is *l so the system plugin knows how large the parameter is.
The final parameter is a pointer (*) to a 64 bit number (l) with no input (.) and we request the output to be stored in $1 (r1).
The system plugin calls the native Windows API so it is often useful to look at MSDN to see what it has to say about the function you are calling.
Given that $0..$9 and $R0..$R9 are NSIS registers, the notation r0 -> r9 (resp. R10..R19 or also R0..R9) is used by the System plugin to specify the $0..$9 (resp. $R0..$R9) registers as a source and / or a destination with system API or other dll function calls.
Either one of source or destination can also be a dot (.) if it is not needed.
Look for the "Calling functions" and "Available sources and destinations" sections in the system plugin documentation.
Related
I am trying to declare a new variable in vvar.h and define it near my new VDSO function. So that I could use this variable in my vdso function.
I have a question about VVar. According to the description in arch/x86/include/asm/vvar.h, when I declare here a new variable as DECLARE_VVAR(0, int, count), I should use DEFINE_VVAR(type, name) to define this variable somewhere else.
The problem is after I defined this variable somewhere else, like DEFINE_VVAR(int, count), when I am trying to assign an integer value to this variable count, it is failed. This is because after kernel version 5.2 #define DEFINE_VVAR(type, name) has been changed from #define DEFINE_VVAR(type, name) type name to #define DEFINE_VVAR(type, name) type name[CS_BASES]. Right now this variable count is an integer array instead of type integer. Therefore I can't assign a integer value to it. Do you know how to fix it?
VVAR.h: https://elixir.bootlin.com/linux/v5.12/source/arch/x86/include/asm/vvar.h#L43
Typically, you cannot add a variable simply through DECLARE_VVAR macro.
The first thing you have to be aware of is that .vvar is a page of memory located inside the memory (more specifically, before .vdso) and could access by both kernel and userland. You can see this inside the linker script https://elixir.bootlin.com/linux/latest/source/arch/x86/entry/vdso/vdso-layout.lds.S. For now, kernel already has a data structure `struct video to format the data inside this page.
Second, assume you want to add a variable inside the .vvar page and access it in your new vdso function, the easiest way is to add it inside the sturct vdso structure of include/vdso/datapage.h: https://elixir.bootlin.com/linux/latest/source/include/vdso/datapage.h. After that, you can update them inside the kernel (for example, in schedule) in the same way as other vvar variables.
Second, if you want to own your own vvar page, you have to define your own vvar data structure inside the datapage.h and do not forget DEFINE_VVAR in vsyscall.h: https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/vdso/vsyscall.h ALso, since the vvar memory layout is compact, you also need to allocate another page through linker script: https://elixir.bootlin.com/linux/latest/source/arch/x86/entry/vdso/vdso-layout.lds.S by change vvar_start = . - 4 * PAGE_SIZE; into vvar_start = . - 5 * PAGE_SIZE;
I am trying to understand a small read function in my program I am trying to decipher.The code is below
READ (LREST, END=350, ERR=350) ICHR
IF (ICHR .EQ. ICKLNK) THEN
DO L = 1, 4
READ (LREST, END=350, ERR=350)
ENDDO
So basically LREST is some kind of an argument provided for this subroutine this function is in. However, I found that LREST is not defined anywhere(used grep to see where LREST is defined in my *.f files. So my questions is what is that LREST doing there in READ function. I thought the location LREST is at is where the unit is defined.
Second questions is that ICHR is a 16 character string variable define for this subroutine. However, the contents of ICHR have not been assigned. I have no idea what this READ function is trying to read from.
Going over to IF statement, ICKLNK is another 16 character string variable with a defined strings. Because ICHR is not defined, does that mean this if statement never gets entered in?
Finally, the do loop( or for loop) has variable L in it but it is not even being used for read function inside of the loop.
Im a beginning in fortran so I may just be lacking a very basic knowledge but if you know an answer to my question please let me know. Thanks!
Han
You are correct that LREST is specifying the unit number (or internal file if it is character). You seem to suggest that LREST is an argument in this subroutine or function, which means its value is passed in by whoever calls the function. Showing us only a small piece of the code makes it hard to provide further details.
Again, you say ICHR is an argument to the procedure, so it takes on the value of whatever was passed in by the call. ICLNK is probably similar, but you didn't show all the code.
The DO (not for) loop is using L just as a counter; it doesn't need to be referenced inside the loop.
In NSIS, I was going through some API which calls some functions of Windows.
For example:
Kernel32::SetEnvironmentVariable(t, t)i ("VAR1", "$R0").r0
Kernel32::GetLocaleInfo(i,i,t,i)i(2048,0x2,.r0,${NSIS_MAX_STRLEN})i
As I am a newbie, can anyone please explain what is happening here ? What is t , i , .ro etc. ?
I know that kernel32 is a DLL and SetEnvironmentVariable & GetLocaleInfo are the functions defined in this DLL.
The documentation will tell you what t and i are.
The basic syntax for System::Call is module::function(parameters)return. Parameters and return are optional and they both follow the same basic format: type input output. Type is not optional if you need input and/or output.
Parameters and return can be repeated multiple times, this is only useful when the function prototype exists as a define but you sometimes see this syntax on the NSIS Wiki as well. Kernel32::SetEnvironmentVariable(t, t)i ("VAR1", "$R0").r0 is exactly the same as Kernel32::SetEnvironmentVariable(t "VAR1", t "$R0")i.r0. When using a define it would look like this:
!define SetEnvironmentVariable "Kernel32::SetEnvironmentVariable(t, t)i"
System::Call '${SetEnvironmentVariable}("VAR1", "$R0").r0'
It is helpful if you know a language like C/C++ or Delphi when using the System plug-in because you need to understand the basic Windows types and how functions are typically used.
If for example you wanted to call the GetWindowsDirectory function you would first look at the function on MSDN:
UINT WINAPI GetWindowsDirectory(_Out_ LPTSTR lpBuffer, _In_ UINT uSize);
Translating this to NSIS gives you a initial skeleton of Kernel32::GetWindowsDirectory(t, i)i (WINAPI is the default calling convention). t maps to the Windows TCHAR* type and i is a 32-bit integer, the same as ULONG, LONG, DWORD, INT, UINT, INT32 and UINT32 in the Windows SDK.
The only missing piece is the input and output. The MSDN declaration is decorated with SAL annotations so it is easy to see which parameters are input and which are output.
A working example might look something like this:
System::Call 'Kernel32::GetWindowsDirectory(t .r1, i ${NSIS_MAX_STRLEN})i .r0'
DetailPrint "Return=$0 Output1=$1"
. can be used as a "nothing" placeholder when there is no input. In this case we use it twice. The return value never has input when using System::Call and we also have one output-only parameter.
r1 and r0 are aliases for the $1 and $0 NSIS registers and these aliases must be used when you need the output of something. The plain NSIS register can be used as input but then the variable expansion happens inside NSIS and not in the plug-in and this can cause issues with quotes in strings and is not really recommended if the string might contain quotes or legal System plug-in syntax. I would therefore recommend that your first example is rewritten as Kernel32::SetEnvironmentVariable(t "VAR1", t R0)i.r0.
In C your second example would look something like
char mybuf[1024];
GetLocaleInfo(MAKELCID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT), LOCALE_SLANGUAGE, mybuf, sizeof(mybuf));
My program, PKGDAYMONR has the control option:
ctl-opt Main( CheckDailyPackages )
The CheckDailyPackages procedure has the following PI:
dcl-pi *n ExtPgm( 'PGMNAME' );
As you can see the ExtPgm parameter is not the name of the program. In fact, it’s what came over in the template source and I forgot to change it. Despite the wrong name in ExtPgm, the program runs without a problem.
If I remove that parameter and leave the keyword as just ExtPgm, I get the following message:
RNF3573: A parameter is required for the EXTPGM keyword when the
procedure name is longer than 10.
If I drop ExtPgm from the Procedure Interface altogether, it also complains:
RNF3834: EXTPGM must be specified on the prototype for the MAIN()
procedure.
So why is it that I have to specify a parameter if it doesn't matter what value I enter?
O/S level: IBM i 7.2
Probably worth pursuing as a defect with the service provider; presumably for most, that would be IBM rather than a third-party, as they would have to contact IBM anyhow, given the perceived issue is clearly with their compiler. Beyond that, as my "Answer", I offer some thoughts:
IMO, and in apparent agreement with the OP, naming the ExtPgm seems pointless in the given scenario. I think the compiler is confused while trying to enforce some requirements in validations of the implicitly generated Prototype for the linear-main for which only a Procedure Interface is supplied; i.e. enforcing requirements that are appropriate for an explicit Prototype, but requirements that could be overlooked [thus are no longer requirements] in the given scenario.? I am suggesting that while the RNF3573 would seem appropriate for diagnosing EXTPGM specifications of an explicit Prototype, IMO that same effect is inappropriate [i.e. the validation should not be performed] for an implicit prototype that was generated by the compiler.
FWiW: Was the fixed-format equivalent of that free-form code tested, to see if the same or a different error was the effect? The following source code currently includes the EXTPGM specification with 'PGMNAME' as the argument [i.e. supplying any bogus value of 10-byte naming to supplicate the compiler, just as is being done in the scenario of the OP, solely to effect a successful compile], but could be compiled with the other variations with changes to the source, mimicking what was done with free-form variations, to test if the same\consistent validations and errors are the effect:
- just EXTPGM keyword coded (w/out argument); is RNF3573 the effect?
- the EXTPGM keyword could be omitted; is RNF3834 the effect?
- the D-spec removed entirely (if there are no parameters defined); ¿that was not one of the variations noted in the OP as being tried, so... the effect?
H MAIN(CheckDailyPackages)
*--------------------------------------------------
* Program name: CheckDailyPackages (PGMNAME)
*--------------------------------------------------
P CheckDailyPackages...
P B
D PI EXTPGM('PGMNAME')
/free
// Work is done here
/end-free
P CheckDailyPackages...
P E
I got a response from IBM and essentially Biswa was on to something, it simply wasn't clear (in my opinion) about the answer.
Essentially the EXTPGM is required on long Main procedure names in order to support recursive program calls.
This is the response I received from IBM explaining the reason for the scenario:
The incorrect EXTPGM would only matter if there was a call to the main
procedure (the program) within the module.
When the compiler processes the procedure interface, it doesn't know
whether there might be a call that appears later in the module.
EXTPGM keyword is used to define the external name of the program which you want to prototype. If you mention the EXTPGM then the program will be called dynamically.
Let us take an example in order to explain your query.
PGMA
D cmdExc PR ExtPgm('QSYS/QCMDEXC')
D 200A const
D 15P05 const
c callp cmdExc('CLRPFM LIB1/PF1':200)
C Eval *INLR = *ON
In the above example CmdExc used for the dynamic call to QSYS/QCMDEXC.
When we use the same program name as the EXTPGM parameter it acts as an entry point to the program when called from other programs or procedure.
But in any case when we mention any name as the sample parameter the EXTPGM will not give any error in compilation, but it gives the error during run time as it tries to resolve the name during run time.
I am totally confused about the following macro modifier in a Makefile I have come onto,
TOOLS = $(TOOL_ROOTS:%=$(OBJDIR)%$(TOOL_SUFFIX))
Here
TOOL_ROOTS=some filename prefixes
OBJDIR=$HOME/obj/
TOOL_SUFFIX=.so
Can someone tell me what this line actually means?
TOOL_ROOTS must be getting assigned some value other than the empty string at some point or that does nothing (which I'll show in a moment).
So first things first just expanding the variables takes us from:
TOOLS = $(TOOL_ROOTS:%=$(OBJDIR)%$(TOOL_SUFFIX))
to:
TOOLS = $(:%=~/obj%.so)
(which we can immediately see doesn't look right, and as I'll explain in a moment doesn't do anything)
So lets pretend it has a value instead.
TOOL_ROOTS = shovel axe hammer
And try the expansion again:
TOOLS = $(shovel axe hammer:%=~/obj%.so)
(That OBJDIR definition looks odd also. I would expect it to be ~/obj/ or something... and that's ignoring that ~ is a bad choice here and that $HOME would be much better.)
The next thing we need to know is what that syntax is all about. Well it is a Substitution Reference.
A substitution reference substitutes the value of a variable with alterations that you specify. It has the form ‘$(var:a=b)’ (or ‘${var:a=b}’) and its meaning is to take the value of the variable var, replace every a at the end of a word with b in that value, and substitute the resulting string.
When we say “at the end of a word”, we mean that a must appear either followed by whitespace or at the end of the value in order to be replaced; other occurrences of a in the value are unaltered. For example:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
sets ‘bar’ to ‘a.c b.c c.c’. See Setting Variables.
A substitution reference is actually an abbreviation for use of the patsubst expansion function (see Functions for String Substitution and Analysis). We provide substitution references as well as patsubst for compatibility with other implementations of make.
Another type of substitution reference lets you use the full power of the patsubst function. It has the same form ‘$(var:a=b)’ described above, except that now a must contain a single ‘%’ character. This case is equivalent to ‘$(patsubst a,b,$(var))’. See Functions for String Substitution and Analysis, for a description of the patsubst function.
For example:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
sets ‘bar’ to ‘a.c b.c c.c’.
So, the first % there is matching the entirety of every word in the value of the variable (here shovel axe hammer) and then replacing each value with the expansion of the second part.
So shovel becomes ~/objshovel.so, etc. and we end up with:
TOOLS = ~/objshovel.so ~/objaxe.so ~/objhammer.so
See what I meant about OBJDIR being odd before? OBJDIR=~/obj/ would have left us with this instead:
TOOLS = ~/obj/shovel.so ~/obj/axe.so ~/obj/hammer.so
which makes a lot more sense to me.