Author Topic: Arithmetic on $ -> error: division operator may only be applied to scalar values  (Read 8014 times)

Offline laalmf

  • Jr. Member
  • *
  • Posts: 5
Minimal PoC:
Code: [Select]
$ cat test.asm
%define ROUNDUP(a, b) (b * ((a + b - 1)/b))
db 0
dd ROUNDUP($, 256)
$ nasm -f bin -o test.bin test.asm
test.asm:3: error: division operator may only be applied to scalar values
« Last Edit: May 09, 2018, 02:14:37 PM by laalmf »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Hi laalmf,
Welcome to the Forum. I suppose the question is, "What's a scalar value?" The documentation doesn't tell us that. The issue is that the value of "$" - or any label - is not known at assemble-time. It is determined by the linker. In the case of "-f bin", Nasm acts as its own linker... but the value still isn't known at assemble-time. The difference between two labels is known - is a "scalar value". "$$" is the beginning of the section/segment. (zero, or whatever "org" you've given) So ($ - $$) will give you the value you want for "$", and Nasm will be happy with it. If "$" is not in the first section, you may need to add a label at the start of your code. In any case, "the difference between two labels" is what Nasm is looking for.

Best,
Frank


Offline laalmf

  • Jr. Member
  • *
  • Posts: 5
Hi Frank,

thank you for your answer. I did some further research on the topic and also found some other forum posts explaining the issue similar to your answer. While I do now understand the underlying problem, I still think that this is somewhat of a shortcoming of nasm. I think that generalizing the concept of a linker to the "-f bin"case is a little bit confusing to say the least. I can totaly see why my example would be problematic for object files for example, as the external linker would have no way of dealing with such a situation. However, in the "-f bin" case case this can easily be dealt with in theory as as all the necessary information is present and no external linker will ever have to deal with the file. Anyways, this is all theory and I know that this is not gonna change just because of me, so I'll just leave it at that ;).

However, your solution of adding a label in the beginning does not always seem to work. Let's say I have something like:
Code: [Select]
%define ROUNDUP(a, b) (b * ((a + b - 1)/b))
base equ 0x400000
org base

dd totalsize
section test progbits valign=0x1000 align=0x200
db 42
totalsize equ ROUNDUP($ - base, 0x1000)
Here, I want to emit the virtual size of the image rounded up to the next multiple of the page size (0x1000) right before the section.
The image base is 0x400000 and the section starts at 0x401000 and contains 1 byte, which means toalsize should be 0x2000. Now this example fails for the reasons you described in your answer. However, your solution of adding a label also does not work:
Code: [Select]
$ cat test.asm
%define ROUNDUP(a, b) (b * ((a + b - 1)/b))
base equ 0x400000
org base
start:

dd totalsize
section test progbits valign=0x1000 align=0x200
db 42
totalsize equ ROUNDUP($ - start, 0x1000)
$ nasm -f bin -o test.bin test.asm
test.asm:9: error: division operator may only be applied to scalar values
Which is not really surprising given that nasm also couldn't handle the "- base" case. I guess the problem is that nasm can't figure out the delta because of the section.

Is there any way this can be solved?
« Last Edit: May 09, 2018, 07:10:37 PM by laalmf »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Oh, my! This is uglier than a bulldog's hind end. I think it does what you want, but may not work as the code gets larger. I tried to use "section.test.start" but apparently it's a "non-scalar value", too. I "equ"ed valign. I don't think the align value comes into this (right?). This is as far as I could get with it (at the moment). I'm willing to work with you more if this doesn't help...

Code: [Select]
%define ROUNDUP(a, b) (b * ((a + b - 1)/b))
base equ 0x400000
org base
start:

dd totalsize

end:

test_align equ 0x1000
section test progbits valign=test_align align=0x200

db 42

totalsize equ ROUNDUP(($ - $$ ) + (end - start) + test_align , 0x1000)

Best,
Frank




Offline laalmf

  • Jr. Member
  • *
  • Posts: 5
Hi Frank,

thank you for your answer again. However, as you observed, your solution will not always work, i.e., in the following case it will fail:
Code: [Select]
%define ROUNDUP(a, b) (b * ((a + b - 1)/b))
base equ 0x400000
org base
start:

dd totalsize
times 2045 db 0
end:

test_align equ 0x1000
section test progbits valign=test_align align=0x200
times 2048 db 0

totalsize equ ROUNDUP(($ - $$ ) + (end - start) + test_align , 0x1000)
Here, nothing really changes with respect to section granularity. The section still starts at 0x401000 and consists of 2048 bytes, i.e., totalsize should still be 0x2000. However, it is 0x3000. The problem is that your solution uses "test_align" in the computation to compensate for the padding, which is, however, inaccurate and as far as I can tell one cannot really know the padding at this point.

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
can it be simplified to something like this?

Code: [Select]
and eax,-0x1000
add eax,0x1000

Offline laalmf

  • Jr. Member
  • *
  • Posts: 5
I think you're confusing something here. I want to calculate those values at assemble-time, not at runtime. Hence, your solution of adding x86 intructions is not gonna help.

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
I think you're confusing something here. I want to calculate those values at assemble-time, not at runtime. Hence, your solution of adding x86 intructions is not gonna help.
Don't use the register then. Use it as constant and build your expression around that technique of getting to the next 0x1000.


 

Offline laalmf

  • Jr. Member
  • *
  • Posts: 5
You're really confusing some fundamentals. Adding x86 instructions will not solve my problem. This has absolutely nothing to to with emitting machine code.

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
You're really confusing some fundamentals. Adding x86 instructions will not solve my problem. This has absolutely nothing to to with emitting machine code.
As I mentioned, you don't have to use AND instruction. There's an AND operator that does the same job. I was thinking something similar to this one below. This is probably not what you'd wanted, but it is close to what u want to achieve.

Code: [Select]
%define b 0x1000
%define SOMETHING ((($-$$) & -b) + b)

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
I constructed a proof-of-concept code to demonstrate how you can build an arithmetic expression without the "scalar" errors and the size rounded up to the next 0x1000s.

Code: [Select]
        global _WinMain@16
        %define b 0x1000
        extern _printf

        section .data
x:      TIMES 4095 db 0
tester: dd (x + (($-$$) & -b) + b)
f:      db 'x is at %p. tester = %p',10,0

        section .text
_WinMain@16:
        enter 0,0
        push dword[tester]
        push x
        push f
        call _printf
        add esp,12
        pop ebp
        ret

yields;

Code: [Select]
x is at 00402000. tester = 00403000
Note if you changed the size of "x" to 4097, then "tester" will be incremented to 00404000 instead - the effect that you wanted (I think).

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Hi Guys,

I'm getting more and more confused as we go along. Can we calculate the size of the padding separately like so?

Code: [Select]
dd totalsize
times 2045 db 0
end1:

test_align equ 0x1000
section test progbits valign=test_align align=0x200
start2:
times 2048 db 0
end2:

pad equ ROUNDUP( (end1 - start1), 0x1000) - (end1 - start1)
totalsize equ ROUNDUP( (end1 - start1 ) + (end2 - start2) + pad , 0x1000

I'm not really certain what's supposed to be included in "totalsize". Should it be just the size of section .text plus the size of section test? If we're including padding, should it be physical (file size) or virtual?

Best,
Frank