src/video/SDL_stretch.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 18 Oct 2009 17:31:37 +0000
branchSDL-1.2
changeset 4355 9b464226e541
parent 4159 a1b03ba2fcd0
child 4356 ab2dfac9d5c1
permissions -rw-r--r--
Fixed bug #855

Ludwig Nussel 2009-10-18 06:31:52 PDT

an mprotect call was added to fix bug 528. However that results in a buffer
that allows writing and code execution. Ie the no-execute security features of
modern operating systems are defeated this way. Two mprotect calls are needed.
One to make the buffer executable but not writeable when done and another one
to make the buffer writeable again if the content needs to be changed.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* This a stretch blit implementation based on ideas given to me by
    25    Tomasz Cejner - thanks! :)
    26 
    27    April 27, 2000 - Sam Lantinga
    28 */
    29 
    30 #include "SDL_video.h"
    31 #include "SDL_blit.h"
    32 
    33 /* This isn't ready for general consumption yet - it should be folded
    34    into the general blitting mechanism.
    35 */
    36 
    37 #if ((defined(_MFC_VER) && defined(_M_IX86)/* && !defined(_WIN32_WCE) still needed? */) || \
    38      defined(__WATCOMC__) || \
    39      (defined(__GNUC__) && defined(__i386__))) && SDL_ASSEMBLY_ROUTINES
    40 #define USE_ASM_STRETCH
    41 #endif
    42 
    43 #ifdef USE_ASM_STRETCH
    44 
    45 #ifdef HAVE_MPROTECT
    46 #include <sys/types.h>
    47 #include <sys/mman.h>
    48 #endif
    49 #ifdef __GNUC__
    50 #define PAGE_ALIGNED __attribute__((__aligned__(4096)))
    51 #else
    52 #define PAGE_ALIGNED
    53 #endif
    54 
    55 #if defined(_M_IX86) || defined(i386)
    56 #define PREFIX16	0x66
    57 #define STORE_BYTE	0xAA
    58 #define STORE_WORD	0xAB
    59 #define LOAD_BYTE	0xAC
    60 #define LOAD_WORD	0xAD
    61 #define RETURN		0xC3
    62 #else
    63 #error Need assembly opcodes for this architecture
    64 #endif
    65 
    66 static unsigned char copy_row[4096] PAGE_ALIGNED;
    67 
    68 static int generate_rowbytes(int src_w, int dst_w, int bpp)
    69 {
    70 	static struct {
    71 		int bpp;
    72 		int src_w;
    73 		int dst_w;
    74 		int status;
    75 	} last;
    76 
    77 	int i;
    78 	int pos, inc;
    79 	unsigned char *eip;
    80 	unsigned char load, store;
    81 
    82 	/* See if we need to regenerate the copy buffer */
    83 	if ( (src_w == last.src_w) &&
    84 	     (dst_w == last.dst_w) && (bpp == last.bpp) ) {
    85 		return(last.status);
    86 	}
    87 	last.bpp = bpp;
    88 	last.src_w = src_w;
    89 	last.dst_w = dst_w;
    90 	last.status = -1;
    91 
    92 	switch (bpp) {
    93 	    case 1:
    94 		load = LOAD_BYTE;
    95 		store = STORE_BYTE;
    96 		break;
    97 	    case 2:
    98 	    case 4:
    99 		load = LOAD_WORD;
   100 		store = STORE_WORD;
   101 		break;
   102 	    default:
   103 		SDL_SetError("ASM stretch of %d bytes isn't supported\n", bpp);
   104 		return(-1);
   105 	}
   106 #ifdef HAVE_MPROTECT
   107 	/* Make the code writeable */
   108 	if ( mprotect(copy_row, sizeof(copy_row), PROT_READ|PROT_WRITE) < 0 ) {
   109 		SDL_SetError("Couldn't make copy buffer writeable");
   110 		return(-1);
   111 	}
   112 #endif
   113 	pos = 0x10000;
   114 	inc = (src_w << 16) / dst_w;
   115 	eip = copy_row;
   116 	for ( i=0; i<dst_w; ++i ) {
   117 		while ( pos >= 0x10000L ) {
   118 			if ( bpp == 2 ) {
   119 				*eip++ = PREFIX16;
   120 			}
   121 			*eip++ = load;
   122 			pos -= 0x10000L;
   123 		}
   124 		if ( bpp == 2 ) {
   125 			*eip++ = PREFIX16;
   126 		}
   127 		*eip++ = store;
   128 		pos += inc;
   129 	}
   130 	*eip++ = RETURN;
   131 
   132 	/* Verify that we didn't overflow (too late!!!) */
   133 	if ( eip > (copy_row+sizeof(copy_row)) ) {
   134 		SDL_SetError("Copy buffer overflow");
   135 		return(-1);
   136 	}
   137 #ifdef HAVE_MPROTECT
   138 	/* Make the code executable but not writeable */
   139 	if ( mprotect(copy_row, sizeof(copy_row), PROT_READ|PROT_EXEC) < 0 ) {
   140 		SDL_SetError("Couldn't make copy buffer executable");
   141 		return(-1);
   142 	}
   143 #endif
   144 	last.status = 0;
   145 	return(0);
   146 }
   147 
   148 #endif /* USE_ASM_STRETCH */
   149 
   150 #define DEFINE_COPY_ROW(name, type)			\
   151 void name(type *src, int src_w, type *dst, int dst_w)	\
   152 {							\
   153 	int i;						\
   154 	int pos, inc;					\
   155 	type pixel = 0;					\
   156 							\
   157 	pos = 0x10000;					\
   158 	inc = (src_w << 16) / dst_w;			\
   159 	for ( i=dst_w; i>0; --i ) {			\
   160 		while ( pos >= 0x10000L ) {		\
   161 			pixel = *src++;			\
   162 			pos -= 0x10000L;		\
   163 		}					\
   164 		*dst++ = pixel;				\
   165 		pos += inc;				\
   166 	}						\
   167 }
   168 DEFINE_COPY_ROW(copy_row1, Uint8)
   169 DEFINE_COPY_ROW(copy_row2, Uint16)
   170 DEFINE_COPY_ROW(copy_row4, Uint32)
   171 
   172 /* The ASM code doesn't handle 24-bpp stretch blits */
   173 void copy_row3(Uint8 *src, int src_w, Uint8 *dst, int dst_w)
   174 {
   175 	int i;
   176 	int pos, inc;
   177 	Uint8 pixel[3] = { 0, 0, 0 };
   178 
   179 	pos = 0x10000;
   180 	inc = (src_w << 16) / dst_w;
   181 	for ( i=dst_w; i>0; --i ) {
   182 		while ( pos >= 0x10000L ) {
   183 			pixel[0] = *src++;
   184 			pixel[1] = *src++;
   185 			pixel[2] = *src++;
   186 			pos -= 0x10000L;
   187 		}
   188 		*dst++ = pixel[0];
   189 		*dst++ = pixel[1];
   190 		*dst++ = pixel[2];
   191 		pos += inc;
   192 	}
   193 }
   194 
   195 /* Perform a stretch blit between two surfaces of the same format.
   196    NOTE:  This function is not safe to call from multiple threads!
   197 */
   198 int SDL_SoftStretch(SDL_Surface *src, SDL_Rect *srcrect,
   199                     SDL_Surface *dst, SDL_Rect *dstrect)
   200 {
   201 	int src_locked;
   202 	int dst_locked;
   203 	int pos, inc;
   204 	int dst_width;
   205 	int dst_maxrow;
   206 	int src_row, dst_row;
   207 	Uint8 *srcp = NULL;
   208 	Uint8 *dstp;
   209 	SDL_Rect full_src;
   210 	SDL_Rect full_dst;
   211 #ifdef USE_ASM_STRETCH
   212 	SDL_bool use_asm = SDL_TRUE;
   213 #ifdef __GNUC__
   214 	int u1, u2;
   215 #endif
   216 #endif /* USE_ASM_STRETCH */
   217 	const int bpp = dst->format->BytesPerPixel;
   218 
   219 	if ( src->format->BitsPerPixel != dst->format->BitsPerPixel ) {
   220 		SDL_SetError("Only works with same format surfaces");
   221 		return(-1);
   222 	}
   223 
   224 	/* Verify the blit rectangles */
   225 	if ( srcrect ) {
   226 		if ( (srcrect->x < 0) || (srcrect->y < 0) ||
   227 		     ((srcrect->x+srcrect->w) > src->w) ||
   228 		     ((srcrect->y+srcrect->h) > src->h) ) {
   229 			SDL_SetError("Invalid source blit rectangle");
   230 			return(-1);
   231 		}
   232 	} else {
   233 		full_src.x = 0;
   234 		full_src.y = 0;
   235 		full_src.w = src->w;
   236 		full_src.h = src->h;
   237 		srcrect = &full_src;
   238 	}
   239 	if ( dstrect ) {
   240 		if ( (dstrect->x < 0) || (dstrect->y < 0) ||
   241 		     ((dstrect->x+dstrect->w) > dst->w) ||
   242 		     ((dstrect->y+dstrect->h) > dst->h) ) {
   243 			SDL_SetError("Invalid destination blit rectangle");
   244 			return(-1);
   245 		}
   246 	} else {
   247 		full_dst.x = 0;
   248 		full_dst.y = 0;
   249 		full_dst.w = dst->w;
   250 		full_dst.h = dst->h;
   251 		dstrect = &full_dst;
   252 	}
   253 
   254 	/* Lock the destination if it's in hardware */
   255 	dst_locked = 0;
   256 	if ( SDL_MUSTLOCK(dst) ) {
   257 		if ( SDL_LockSurface(dst) < 0 ) {
   258 			SDL_SetError("Unable to lock destination surface");
   259 			return(-1);
   260 		}
   261 		dst_locked = 1;
   262 	}
   263 	/* Lock the source if it's in hardware */
   264 	src_locked = 0;
   265 	if ( SDL_MUSTLOCK(src) ) {
   266 		if ( SDL_LockSurface(src) < 0 ) {
   267 			if ( dst_locked ) {
   268 				SDL_UnlockSurface(dst);
   269 			}
   270 			SDL_SetError("Unable to lock source surface");
   271 			return(-1);
   272 		}
   273 		src_locked = 1;
   274 	}
   275 
   276 	/* Set up the data... */
   277 	pos = 0x10000;
   278 	inc = (srcrect->h << 16) / dstrect->h;
   279 	src_row = srcrect->y;
   280 	dst_row = dstrect->y;
   281 	dst_width = dstrect->w*bpp;
   282 
   283 #ifdef USE_ASM_STRETCH
   284 	/* Write the opcodes for this stretch */
   285 	if ( (bpp == 3) ||
   286 	     (generate_rowbytes(srcrect->w, dstrect->w, bpp) < 0) ) {
   287 		use_asm = SDL_FALSE;
   288 	}
   289 #endif
   290 
   291 	/* Perform the stretch blit */
   292 	for ( dst_maxrow = dst_row+dstrect->h; dst_row<dst_maxrow; ++dst_row ) {
   293 		dstp = (Uint8 *)dst->pixels + (dst_row*dst->pitch)
   294 		                            + (dstrect->x*bpp);
   295 		while ( pos >= 0x10000L ) {
   296 			srcp = (Uint8 *)src->pixels + (src_row*src->pitch)
   297 			                            + (srcrect->x*bpp);
   298 			++src_row;
   299 			pos -= 0x10000L;
   300 		}
   301 #ifdef USE_ASM_STRETCH
   302 		if (use_asm) {
   303 #ifdef __GNUC__
   304 			__asm__ __volatile__ (
   305 			"call *%4"
   306 			: "=&D" (u1), "=&S" (u2)
   307 			: "0" (dstp), "1" (srcp), "r" (copy_row)
   308 			: "memory" );
   309 #elif defined(_MSC_VER) || defined(__WATCOMC__)
   310 		{ void *code = copy_row;
   311 			__asm {
   312 				push edi
   313 				push esi
   314 	
   315 				mov edi, dstp
   316 				mov esi, srcp
   317 				call dword ptr code
   318 
   319 				pop esi
   320 				pop edi
   321 			}
   322 		}
   323 #else
   324 #error Need inline assembly for this compiler
   325 #endif
   326 		} else
   327 #endif
   328 		switch (bpp) {
   329 		    case 1:
   330 			copy_row1(srcp, srcrect->w, dstp, dstrect->w);
   331 			break;
   332 		    case 2:
   333 			copy_row2((Uint16 *)srcp, srcrect->w,
   334 			          (Uint16 *)dstp, dstrect->w);
   335 			break;
   336 		    case 3:
   337 			copy_row3(srcp, srcrect->w, dstp, dstrect->w);
   338 			break;
   339 		    case 4:
   340 			copy_row4((Uint32 *)srcp, srcrect->w,
   341 			          (Uint32 *)dstp, dstrect->w);
   342 			break;
   343 		}
   344 		pos += inc;
   345 	}
   346 
   347 	/* We need to unlock the surfaces if they're locked */
   348 	if ( dst_locked ) {
   349 		SDL_UnlockSurface(dst);
   350 	}
   351 	if ( src_locked ) {
   352 		SDL_UnlockSurface(src);
   353 	}
   354 	return(0);
   355 }
   356