1

I am working on a program in C + x86 assembly (NASM) which performs rotating and scaling of an image. In order to do that it goes through pixels of the destination image one by one and calculates the corresponding pixel in the source image.

That part of the assembly code:

; buffer operations
push ebx
fistp dword loc         ; store the number of src pixel
mov   dword ebx, loc    ; move it to ebx
imul ebx, 3             ; 3 bytes per pixel, so multiply pixel number by 3
mov dword eax, [ebx+esi]; store that pixel's color bytes ; ERROR, SEGSEV
mov dword [edi], eax    ; draw a pixel
pop ebx

particularly the line marked 'ERROR, SEGSEV' generates a segmentation fault. I reckon that is due to the fact that I'm trying to access the unaligned memory address. That said, the bmp file pixel buffer is organised in a way that each pixel has B, G, R bytes stored one after another, so 3 bytes per pixel and each pixel's first byte can have a position in memory that is not divisible by 4 (eg: pixel one: 0.B, 1.G, 2.R; pixel two: 3.B, 4.G, 5.R - so I must access the address 3 to get to the second pixel).

The question is: how then can I access pixel's data if I'm not allowed to access unaligned memory location and how is it usually done when working with bmp files?

  • 3
    You don't need to place `dword` in front of a register since the size of a register is always known.`mov ebx, loc` is going to move the **address** of `loc` to _EBX_. Is that what you want or do you want the **value** at `loc` and something like `mov ebx, [loc]`? If you step through with a debugger (ie: gdb) you should find out exactly why this is failing. `mov` can access aligned data fine, it may just incur a performance penalty depending on architecture. – Michael Petch Jun 12 '16 at 02:34
  • 3
    Most x86 operating systems don't enable alignment checking. However since you're reading 4 bytes when you should be only reading 3, you risk reading past the end of the source image and into memory that hasn't been allocated. It's likely that there's going to be something there, but unless you take specific steps to ensure that you could get a fault when trying to read the last pixel in your source image. – Ross Ridge Jun 12 '16 at 02:37
  • In my comment there is a typo "`mov` can access aligned data fine" should have been _UNALIGNED_ – Michael Petch Jun 12 '16 at 02:41
  • To Michael Petch: Sorry for being imprecise: loc isn't a variable name, but a pseudonim for [ebp-44], like that: '%define loc dword [ebp-44]'. – Gregory Zaika Jun 12 '16 at 02:45
  • So, when you hit the SIGSEGV in a debugger, is `ebx+esi` a valid address, like you expect it to be? Also, if you're writing in asm for performance, this is not going to help. Any decent C compiler will avoid push/popping ebx inside a loop like this, and also avoiding a store-forwarding round trip for floating point (by using SSE2 instead of x87). See the [x86 tag wiki](http://stackoverflow.com/tags/x86/info), especially [Agner Fog's guides](http://agner.org/optimize/) if you want to learn how to write asm that isn't slow. – Peter Cordes Jun 12 '16 at 02:48
  • 1
    The main problem is that this isn't a minimal complete verifiable example, and we don't know if you set up _ESI_ correctly and _EDI_, and what environment is this in? Your own kernel? Is it protected mode or real mode? Your best bet to resolve this is to use a debugger, step through the code, and ensure that [ebx+esi] is a valid address. If this code is in real mode you may have bigger issues.I do assume though this is 32-bit protected mode code. – Michael Petch Jun 12 '16 at 03:01
  • 1
    If it's literally a segmentation fault, then it's Unix (probably Linux or OS X). To add to my previous comment; if you're doing anything for every pixel of an image, there's not usually much point spending time on hand-written asm if you aren't going to use SSE or AVX vectors. (This is true even in real mode; I think SSE works in real mode, but [you have to enable it with a control register](http://stackoverflow.com/questions/31563078/how-do-i-enable-sse-for-my-freestanding-bootable-code)) – Peter Cordes Jun 12 '16 at 03:07
  • Thanks everyone for your answers and links. The problem wasn't in accessing unaligned location, but in the miscalculation of the address. **I was convinced that one can't access unaligned bytes, which wasn't the case**. Thank you once again. I will definetely use the materials you provided for future reference. – Gregory Zaika Jun 12 '16 at 04:11

1 Answers1

0

The OP assumed that the x86 architecture cannot access unaligned data (data at an address which is not a multiple of the size of the register being used), which is a common problem in Reduced Instruction Set Computing (RISC) processors. For example:

mov ebx, 0x12345677    ; Note the odd address
mov eax, [ebx]         ; Load a 32-bit register from an odd address

In a RISC architecture like ARM or PowerPC, this can indeed cause problems: either always (ARM) or if not disabled (PowerPC). With the x86 architecture unaligned accesses have always been possible (albeit sometimes at a speed penalty) - and it's only since the '486 that it was even able to be checked; but that check is almost always off.

It turned out that the problem was elsewhere:

mov dword eax, [ebx+esi]; store that pixel's color bytes ; ERROR, SEGSEV

The OP hadn't confirmed that esi held the desired value.

Note, though, that an unaligned access with the x86 can still cause problems. All segments are defined to be a certain size (even if that size is 4GiB). An access near the top of that segment can "overflow" the segment - and unaligned accesses are the easiest way to suffer this.

John Burger
  • 3,662
  • 1
  • 13
  • 23