5

I am trying to understand xmr-stak miner program. I have understood most of it but having hard time visualizing some pointer manipulation in below code. Its been over 10 years for me to go through any CPP program, any help is apprenticed.

So the question is, what is being assigned to following some of the pointer variables like piHashVal, or piNonce. I have commented my assumptions on each line, but not sure if that is correct. Any explanation with example is very much helpful.

void minethd::work_main() { uint64_t* piHashVal; uint32_t* piNonce;

//assign pointer of bResult + 24 bits
piHashVal = (uint64_t*)(result.bResult + 24); 

//assign pointer of bWorkBlob + 39 bit - piNonce is at 39th bit of blob provided by pool.
piNonce = (uint32_t*)(oWork.bWorkBlob + 39); 


        //value at piNonce is increamented by 1 or address of piNonce is increamented by 1?
        *piNonce = ++result.iNonce;

        //value at piHashVal is compared with target
        if (*piHashVal < oWork.iTarget) {
            executor::inst()->push_event(ex_event(result, oWork.iPoolId));
        }

}

Rahul
  • 105
  • 4

2 Answers2

7

piHashVal = (uint64_t*)(result.bResult + 24);

result.bResult is a pointer to some data structure, it's storing the memory address at which the structure begins. We're not interested in the beginning of that data blob, but want to access some 64-bits starting at offset 24. The above expression increments the address by offset 24 and casts the result to pointer to uint64_t. This means that the expression *piHashVal will return value of those 64-bits, interpreted as unsigned int. Note about the offset, how much bytes it offsets will depend on the type of pointer result.bResult. See also: Pointer Arithmetic for Structs

Example when offseting pointers of different types

piNonce = (uint32_t*)(oWork.bWorkBlob + 39);

This does similar, only we're taking the 32-bits starting at offset 39 from beginning of oWork.bWorkBlob data structure.

*piNonce = ++result.iNonce;

Increment the result.iNonce by 1, and assign the resulting value to *piNonce which is the data stored at memory address piNonce. Note that both variables result.iNonce and *piNonce are changed here. By doing this, we surgically changed some 32-bits in the oWork.BworkBlob data structure since the piNonce points to a part of it and we accessed that part directly with *piNonce.

if (*piHashVal < oWork.iTarget) {
    executor::inst()->push_event(ex_event(result, oWork.iPoolId));
}

So here we compare those 64-bits from the start with the target. We're accessing the value *piHashVal stored at memory location piHashVal.

Appendix - Pointer Example

#include <stdio.h>
#define ARLEN 2

int main(void) { unsigned long a[ARLEN]; unsigned long b; unsigned char c;

for(int i=0; i&lt;ARLEN; ++i)
    a[i] = 100 + i*100;

b = &amp;a[0];
/* Note that b = a is equivalent to the above expression, because when a is
 * declared as an array it becomes a pointer but the memory block for the array
 * is allocated at declaration as well. The [] is also known as offset operator.
 * and can be applied to any pointer.
 * After the above assignment, a[i] and b[i] will be equivalent.
**/

c = (char*) b;

printf(&quot;Accessing using offset operator:\n&quot;);
for(int i=0; i&lt;ARLEN; ++i)
    printf(&quot;a[%d] = %lu = 0x%02X;\n&quot;,i,a[i],a[i]);

printf(&quot;Accessing using dereference operator:\n&quot;);
for(int i=0; i&lt;ARLEN; ++i)
    printf(&quot;*(b+%d) = %lu = 0x%02X\n&quot;,i,*(b+i),*(b+i));

printf(&quot;Reading individual bytes of the array using char pointer:\n&quot;);
for(int i=0; i &lt; (ARLEN*sizeof(long)/sizeof(char)); ++i)
    printf(&quot;*(c+%d) = %hhu = 0x%02X;\n&quot;,i,*(c+i),*(c+i));

printf(&quot;Setting odd bytes to 0xFF:\n&quot;);
for(int i=0; i &lt; (ARLEN*sizeof(long)/sizeof(char)); ++i)
{
    if (i%2==1)
        *(c+i)=0xFF;
    printf(&quot;*(c+%d) = %hhu = 0x%02X;\n&quot;,i,*(c+i),*(c+i));
}

printf(&quot;Printing the modified long array:\n&quot;);
for(int i=0; i &lt; ARLEN; ++i)
{
    printf(&quot;a[%d] = %lu = 0x%02X;\n&quot;,i,a[i],a[i]);
}
return 0;

}

Program output (compiled on x86-64 system):

Accessing using offset operator:
a[0] = 100 = 0x64;
a[1] = 200 = 0xC8;
Accessing using dereference operator:
*(b+0) = 100 = 0x64
*(b+1) = 200 = 0xC8
Reading individual bytes of the array using char pointer:
*(c+0) = 100 = 0x64;
*(c+1) = 0 = 0x00;
*(c+2) = 0 = 0x00;
*(c+3) = 0 = 0x00;
*(c+4) = 0 = 0x00;
*(c+5) = 0 = 0x00;
*(c+6) = 0 = 0x00;
*(c+7) = 0 = 0x00;
*(c+8) = 200 = 0xC8;
*(c+9) = 0 = 0x00;
*(c+10) = 0 = 0x00;
*(c+11) = 0 = 0x00;
*(c+12) = 0 = 0x00;
*(c+13) = 0 = 0x00;
*(c+14) = 0 = 0x00;
*(c+15) = 0 = 0x00;
Setting odd bytes to 0xFF:
*(c+0) = 100 = 0x64;
*(c+1) = 255 = 0xFF;
*(c+2) = 0 = 0x00;
*(c+3) = 255 = 0xFF;
*(c+4) = 0 = 0x00;
*(c+5) = 255 = 0xFF;
*(c+6) = 0 = 0x00;
*(c+7) = 255 = 0xFF;
*(c+8) = 200 = 0xC8;
*(c+9) = 255 = 0xFF;
*(c+10) = 0 = 0x00;
*(c+11) = 255 = 0xFF;
*(c+12) = 0 = 0x00;
*(c+13) = 255 = 0xFF;
*(c+14) = 0 = 0x00;
*(c+15) = 255 = 0xFF;
Printing the modified long array:
a[0] = 18374966859414962020 = 0xFF00FF64;
a[1] = 18374966859414962120 = 0xFF00FFC8;
JollyMort
  • 19,934
  • 3
  • 46
  • 105
2

idk anything about monero source code but:

first thing is that if you mean byte 0 as first then say byte 7 is 8th ,not 7th .

second is that when you deal with structures it has alignment.And my bet is that it's not 1 here. It's not so complex stuff if use it in structure you know.But it's complex to understand abstract.

to tell long story short: if you need say offset of the 4th member of structure it is:

count of padding bytes of second and third members(means second_count + third_count) + its sizes + size of first member + 4th_member_padding_bytes_count.

about padding bytes: padding = (align - (offset & (align - 1))) & (align - 1)

where 'offset' means offset of byte next to last_occupied_byte(previous members are deployed already,current is not ,yet). and you'll have more fun counting it 'from screen' then out of seekin bugs after above stuff.

hard-core post about that: https://en.wikipedia.org/wiki/Data_structure_alignment

(unexpected for wikipedia)

and... with bits and addresses on scene in same time

pointer=smth+4;

means smth + 4 bytes(coz of unsigned char* pointers - only thing that gives direct bytes order needed for memory i/o) ,not bits.

there is some info about pointer + smth and how it looks in real code. https://www.cs.uaf.edu/courses/cs301/2014-fall/notes/pointer-arithmetic/

in table's last row QWORD[..] is just differently worded unsigned long long* . It's size of memory block followed by its address.

so with

long* a=some_address;
long* b = a+1;
long  c = *b;

we have: https://en.wikipedia.org/wiki/Addressing_mode#Base_plus_index

which also means specific assembly instruction set.

with unsigned char* we have https://en.wikipedia.org/wiki/Addressing_mode#Base_plus_offset.2C_and_variations

Alex
  • 71
  • 1
  • 4
  • It's not always a byte, though. Isn't it +4 sizeof(type)? – JollyMort Nov 07 '17 at 21:28
  • maybe yeah but it's not bits – Alex Nov 07 '17 at 21:35
  • yup, that's for sure – JollyMort Nov 07 '17 at 21:38
  • Yup, I have putting multiple printf(s) to see what is going on there and yes, +4, +24 or +39 are not bits nor bytes, they are just incrementing pointer address by 1. in my example, bworkBlob is array of uint8 so +39 pushes bWorkBlob[39th ], since 0 to 38th are 39 addresses. Thanks guys!! – Rahul Nov 07 '17 at 21:50
  • 'incrementing pointer address by 1' means exactly bytes. (unsigned char*)some_pointer + 1 .Yeah arrays (within or outside of structures) has no alignment inside it ,it works coz of this. – Alex Nov 07 '17 at 23:09
  • and again +39 is 40th (anyway) – Alex Nov 07 '17 at 23:17
  • coz 1st is +0 . – Alex Nov 07 '17 at 23:47
  • @JollyMort offset term originates from assembler and means bytes. all this array related stuff is about shifting of array index. – Alex Nov 08 '17 at 17:10
  • It doesn't have to be an array, though. As you demonstrate above: you could take a pointer to an array of longs, cast the pointer to char, increment it by 1 sizeof(char) and change 2nd byte of the long :) You could be arbitrarily modifying any data blob like this if it's written as a single block in memory. – JollyMort Nov 08 '17 at 17:49
  • you'll change byte in memory which then may by represented as part of smth. there is no stuff like long and others in machine code.But there is byte ,bit, and address things. – Alex Nov 08 '17 at 18:07
  • although there are instructions which takes SDWORD values as args.I mean it is just abstraction which looks similar to machine code ,not more.It usually used with arrays.troubles caused by mix machine stuff like address and medium-level abstraction. – Alex Nov 08 '17 at 18:22
  • in other words: long (SDWORD in assembler) could be in machine code ,but not in memory. Shortly term offset where it somehow paired with memory address means count of bytes. – Alex Nov 08 '17 at 18:45
  • finally found it: https://en.wikipedia.org/wiki/Addressing_mode#Base_plus_index vs https://en.wikipedia.org/wiki/Addressing_mode#Base_plus_offset.2C_and_variations vs https://en.wikipedia.org/wiki/Addressing_mode#Base_plus_index_plus_offset So you're not right. – Alex Nov 09 '17 at 10:43
  • Semantics. We're not talking about assembler, but C/C++. In the docs it's also referred to as offset, and [] as "offset operator": "In the chapter about arrays, brackets ([]) were explained as specifying the index of an element of the array. Well, in fact these brackets are a dereferencing operator known as offset operator.";

    Anyways, see my updated answer for the example of what I meant. I can't follow you on the low-level stuff, sorry. I only have some school-level C/C++ knowledge.

    – JollyMort Nov 09 '17 at 20:00
  • https://duckduckgo.com/?q=%22offset+operator%22+%22%5B%5D%22&ia=qa – Alex Nov 15 '17 at 18:12