Re: Problem with SBRK not returning from call on bare metal risk

Damien Towning <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <CANJyiQUma51RA4he1kjZycQ20qkzSMp-wqMuqLpXkqcAB2Mprg@mail.gmail.com>
Thank you for the helpful response and the link. Having understood this
helped a lot. I went back to using my own _sbrk and the newlib malloc seems
to work okay with it. I think this is mostly about the variable names for
heap and stack matching what the newlib expects _and_ avoiding any of the
re-entrant stuff.  Am able to load up and free complex memory blocks out of
the heap.

Now what prompted my question to start with is that I am concerned about
implementing functions I do not ( or should not ) implement myself. So for
example I have had to implement the srand and rand functions myself because
those appear to be re-entrant in the newlib.  I've now moved on and got
another question to ask now about the math library which I think I will put
in a separate post :)



On Tue, Mar 7, 2023 at 12:24 AM Dave Nadler <[email protected]> wrote:

> Hi Damien - You need to verify the sbrk you're using actually provides the
> correct addresses.
> In cases I'm familiar with, sbrk is provided by the chip-specific
> toolchain and not newlib.
> Providing your own sbrk is not all bad...
> Also, you need to be sure how newlib was built in the toolchain you are
> using;
> you didn't show us the newlib configuration?
> This might provide some help:
> https://nadler.com/embedded/newlibAndFreeRTOS.html
> Hope this helps!
> Best Regards, Dave
>
> On 3/6/2023 2:36 AM, Damien Towning wrote:
>
> So a bit of a background. I have the bonfire Risc-V 32 core running on a
> T-35 FPGA board. The metal part of this is working great. The processor
> runs. I've got it to turn around the segment display on the board. I've
> also implemented my own _sbrk and got that to work I think consistently
> with malloc. Now in my Risc-v 32 toolchain I have the newlib and I have
> brought that in and with my own wrapped _sbrk I am able to use the system
> defined malloc. But I am not entirely happy with this because I don't want
> to be wrapping every function I encounter. So for example today I found
> myself wrapping srand and rand. So I am pretty sure the reason my
> nano_newlib isn't working is something to do with either not having the
> correct variable names in my firmware.ld or something to do with disabling
> the re-entrant behaviour of newlib. I've included in this email my
> firmware.ld,platform.h,c code example. What happens is I call the _sbrk(0)
> and ( in the simulator I am also running this in ) I never see a bad opcode
> generated. It just seems like the processor goes away and never comes back.
> Does anybody have any suggestions as to what might cause this sort of
> problem ? I'd like to use the sbrk and other funcitons within the newlib
> itself if possible without rolling my own.
>
> ------------- firmware.ld
> MEMORY
> {
>
>    RAM (rwx)    : ORIGIN = 0x0C000000, LENGTH = 512K
> }
>
> ENTRY(_start)
>
> SECTIONS
> {
>     _rombase = 0x0C000000;
>
>   . = 0x0;
>
>   .text : {
>     *(.init)
>     *(.text.*)
>
>   } > RAM
>
>   .data  ALIGN(4) : {
>     *(.rodata.*)
>     *(.rodata)
>     *(.eh_frame)
>     *(.sdata)
>     *(.data)
>     *(.sbss)
>     *(.sbss.*)
>     *(.gnu.linkonce.sb.*)
>     *(.bss)
>     *(.bss.*)
>     *(.gnu.linkonce.b.*)
>     *(COMMON)
>
>     end = .; _end = .; __end = .;
>
>  } > RAM
>
>  _heap_end = _end;
>  _heap_prev_end = _end;
>
> }
>
> PROVIDE(__heap_end = _end);
> PROVIDE(__prev_heap_end = _end);
> PROVIDE(__heap_start = _end+128K);
>
> PROVIDE(__stack_top   = ORIGIN(RAM) + LENGTH(RAM));
> PROVIDE(__stack_start = ORIGIN(RAM) + LENGTH(RAM));
>
> PROVIDE(__heap_size = 128K );
> PROVIDE(__stack_size = 128K );
>
>
>
> /*
> 0x0C000000 +-------------------+
>            |                   |
>            |     ROM / Flash   |
> 0x0C080000 +-------------------+
>            |                   |
>            |      .data        |
>            |-------------------|
>            |     .bss / .sbss  |
>            |-------------------|
>            |        _end       |
>            |-------------------|
>            |        Heap       |
>            |-------------------|
>            |        Stack      |
> 0xXXXXXXXX +-------------------+
> */
> --------------------------------------------------------------------------------------
>
> ---------platform.h
> ----------------------------------------------------------------
> #ifndef BASIC_PLATFORM_H
> #define BASIC_PLATFORM_H
>
> // New Defintions for new bonfire-soc-io core
> #define IO_BASE 0x04000000
> #define SOC_IO_OFFSET 0x10000 // Offset from one I/O Device to the next
> (64K range)
>
> #define UART0_BASE IO_BASE
> #define SPIFLASH_BASE (IO_BASE+SOC_IO_OFFSET)
> #define GPIO_BASE (IO_BASE+3*SOC_IO_OFFSET)
> #define UART1_BASE (IO_BASE+2*SOC_IO_OFFSET)
>
> #define UART_BASE UART0_BASE // Backwards compatiblity
>
> #define MTIME_BASE 0x0FFFF0000
>
> #define DRAM_BASE 0x0
> #define DRAM_SIZE 0
> #define DRAM_TOP  (DRAM_BASE+DRAM_SIZE-1)
> #define SRAM_BASE 0x0C000000
> #define SRAM_SIZE 524288
> #define SRAM_TOP  (SRAM_BASE+SRAM_SIZE-1)
>
> //#define SYSCLK 12000000
> //#define SYSCLK   33333333
> //#define   SYSCLK   24997500
> //#define   SYSCLK   88786000
> #define     EXT_SYSCLK   89015000
>
> #define SYSCLK       24997500
>
> //#define EXT_SYSCLK   92370000  /* For reasons not clear to me when any
> change is made to the interface   */
>   /* configuration in Efinity and a new synthesis is done a clock time will
> */
>   /* be calculated so you must build your efinity project. Take that new
>  */
>   /* time value. Update it here. Rebuild your hex file. Then rebuild your
> */
>   /* efinity project AGAIN
>  */
>
>
> #define TIME_UNIT 1e+9;  // A billion
>
> #ifndef SIMULATOR
> #define SCALAR 1      // Relative Scalar division for efinity build
> #define BAUD_SCALAR 1 // Relative Scalar division for efinity build
> #else
> #define SCALAR 10000    // Relative Scalar division for simulator. Use for
> any of your own timing since
> #define BAUD_SCALAR 20 // simulator runs slower than real hardware
> #endif
>
> #define CLK_PERIOD (TIME_UNIT / EXT_SYSCLK)  // in ns...
>
> //#define DCACHE_SIZE 0 // (2048*4)  // DCache Size in Bytes
>
> // Parameters for SPI Flash
>
> #define FLASHSIZE (8192*1024)
> #define MAX_FLASH_IMAGESIZE (2024*1024) // Max 2MB of flash used for boot
> image
> #define FLASH_IMAGEBASE (1024*3072)  // Boot Image starts at 3MB in Flash
>
> #define PLATFORM_BAUDRATE (115200*BAUD_SCALAR)
>
> #define NO_RECURSION
> #define NO_SYSCALL
> #define NO_FLASH
> #define NO_XMODEM
> #define NO_DRAMTEST
>
> //#define GPIO_TEST
>
> #endif
> ---------------------------------------------------------------------------------------------
>
> ---- sim_hello.c
> ------------------------------------------------------------------------
> // ==============================================
> // T35 S100 Module Test Code
> // Damien Towning - 2022
> // ==============================================
> #include <stdioSo a bit of a background. I have the bonfire Risc-V 32 core
> running on a T-35 FPGA board. The metal part of this is working great. The
> processor runs. I've got it to turn around the segment display on the
> board. I've also implemented my own _sbrk and got that to work I think
> consistently with malloc. Now in my Risc-v 32 toolchain I have the newlib
> and I have brought that in and with my own wrapped _sbrk I am able to use
> the system defined malloc. But I am not entirely happy with this because I
> don't want to be wrapping every function I encounter. So for example today
> I found myself wrapping srand and rand. So I am pretty sure the reason my
> nano_newlib isn't working is something to do with either not having the
> correct variable names in my firmware.ld or something to do with disabling
> the re-entrant behaviour of newlib. I've included in this email my
> firmware.ld,platform.h,c code example. What happens is I call the _sbrk(0)
> and ( in the simulator I am also running this in ) I never see a bad opcode
> generated. It just seems like the processor goes away and never comes back.
> Does anybody have any suggestions as to what might cause this sort of
> problem ? I'd like to use the sbrk and other funcitons within the newlib
> itself if possible without rolling my own.
>
> ------------- firmware.ld
> MEMORY
> {
>
>    RAM (rwx)    : ORIGIN = 0x0C000000, LENGTH = 512K
> }
>
> ENTRY(_start)
>
> SECTIONS
> {
>     _rombase = 0x0C000000;
>
>   . = 0x0;
>
>   .text : {
>     *(.init)
>     *(.text.*)
>
>   } > RAM
>
>   .data  ALIGN(4) : {
>     *(.rodata.*)
>     *(.rodata)
>     *(.eh_frame)
>     *(.sdata)
>     *(.data)
>     *(.sbss)
>     *(.sbss.*)
>     *(.gnu.linkonce.sb.*)
>     *(.bss)
>     *(.bss.*)
>     *(.gnu.linkonce.b.*)
>     *(COMMON)
>
>     end = .; _end = .; __end = .;
>
>  } > RAM
>
>  _heap_end = _end;
>  _heap_prev_end = _end;
>
> }
>
> PROVIDE(__heap_end = _end);
> PROVIDE(__prev_heap_end = _end);
> PROVIDE(__heap_start = _end+128K);
>
> PROVIDE(__stack_top   = ORIGIN(RAM) + LENGTH(RAM));
> PROVIDE(__stack_start = ORIGIN(RAM) + LENGTH(RAM));
>
> PROVIDE(__heap_size = 128K );
> PROVIDE(__stack_size = 128K );
>
>
>
> /*
> 0x0C000000 +-------------------+
>            |                   |
>            |     ROM / Flash   |
> 0x0C080000 +-------------------+
>            |                   |
>            |      .data        |
>            |-------------------|
>            |     .bss / .sbss  |
>            |-------------------|
>            |        _end       |
>            |-------------------|
>            |        Heap       |
>            |-------------------|
>            |        Stack      |
> 0xXXXXXXXX +-------------------+
> */
> --------------------------------------------------------------------------------------
>
> ---------platform.h
> ----------------------------------------------------------------
> #ifndef BASIC_PLATFORM_H
> #define BASIC_PLATFORM_H
>
> // New Defintions for new bonfire-soc-io core
> #define IO_BASE 0x04000000
> #define SOC_IO_OFFSET 0x10000 // Offset from one I/O Device to the next
> (64K range)
>
> #define UART0_BASE IO_BASE
> #define SPIFLASH_BASE (IO_BASE+SOC_IO_OFFSET)
> #define GPIO_BASE (IO_BASE+3*SOC_IO_OFFSET)
> #define UART1_BASE (IO_BASE+2*SOC_IO_OFFSET)
>
> #define UART_BASE UART0_BASE // Backwards compatiblity
>
> #define MTIME_BASE 0x0FFFF0000
>
> #define DRAM_BASE 0x0
> #define DRAM_SIZE 0
> #define DRAM_TOP  (DRAM_BASE+DRAM_SIZE-1)
> #define SRAM_BASE 0x0C000000
> #define SRAM_SIZE 524288
> #define SRAM_TOP  (SRAM_BASE+SRAM_SIZE-1)
>
> //#define SYSCLK 12000000
> //#define SYSCLK   33333333
> //#define   SYSCLK   24997500
> //#define   SYSCLK   88786000
> #define     EXT_SYSCLK   89015000
>
> #define SYSCLK       24997500
>
> //#define EXT_SYSCLK   92370000  /* For reasons not clear to me when any
> change is made to the interface   */
>   /* configuration in Efinity and a new synthesis is done a clock time will
> */
>   /* be calculated so you must build your efinity project. Take that new
>  */
>   /* time value. Update it here. Rebuild your hex file. Then rebuild your
> */
>   /* efinity project AGAIN
>  */
>
>
> #define TIME_UNIT 1e+9;  // A billion
>
> #ifndef SIMULATOR
> #define SCALAR 1      // Relative Scalar division for efinity build
> #define BAUD_SCALAR 1 // Relative Scalar division for efinity build
> #else
> #define SCALAR 10000    // Relative Scalar division for simulator. Use for
> any of your own timing since
> #define BAUD_SCALAR 20 // simulator runs slower than real hardware
> #endif
>
> #define CLK_PERIOD (TIME_UNIT / EXT_SYSCLK)  // in ns...
>
> //#define DCACHE_SIZE 0 // (2048*4)  // DCache Size in Bytes
>
> // Parameters for SPI Flash
>
> #define FLASHSIZE (8192*1024)
> #define MAX_FLASH_IMAGESIZE (2024*1024) // Max 2MB of flash used for boot
> image
> #define FLASH_IMAGEBASE (1024*3072)  // Boot Image starts at 3MB in Flash
>
> #define PLATFORM_BAUDRATE (115200*BAUD_SCALAR)
>
> #define NO_RECURSION
> #define NO_SYSCALL
> #define NO_FLASH
> #define NO_XMODEM
> #define NO_DRAMTEST
>
> //#define GPIO_TEST
>
> #endif
> ---------------------------------------------------------------------------------------------
>
> ---- sim_hello.c
> ------------------------------------------------------------------------
> // ==============================================
> // T35 S100 Module Test Code
> // Damien Towning - 2022
> // ==============================================
> #include <stdio.h>
> #include <stdlib.h>
>
> //#include <newlib.h>
> //#include <nano_malloc.h>
> #include <errno.h>
> #include <sys/types.h>
> #include <string.h>
> #include "bonfire.h"
> #include "uart.h"
> #include "bonfire_gpio.h"
> #include "mem_rw.h"
> #include "console.h"
>
> extern void* sbrk(incr);
>
>
> // ========================================
> // Main function
> // ========================================
> int main() {
>
>   setBaudRate(PLATFORM_BAUDRATE);
>
>   caddr_t *ptr = sbrk(0);
>   // code never returns from sbrk
>   if (ptr == (void *)-1) {
>     printk("Failed to allocate memory\r\n");
>   }
>   else {
>     printk("Allocated memory at : %p\r\n", (void *) &ptr);
>   }
>   return 0;
> }
> ------------------------------------------------------------------------------------
> --- Makefile
> --------------------------------------------------------------------
>
> .PHONY: all clean
>
> ARCH ?= rv32ia
> ABI=ilp32
> PLATFORM ?= S100_T35
>
> #TARGET_PREFIX ?= riscv32-unknown-linux-gnu
> TARGET_PREFIX ?= riscv32-unknown-elf
> TARGET_CC := $(TARGET_PREFIX)-gcc
> TARGET_LD := $(TARGET_PREFIX)-gcc
> TARGET_SIZE := $(TARGET_PREFIX)-size
> TARGET_OBJCOPY := $(TARGET_PREFIX)-objcopy
> HEXDUMP ?= hexdump
> DATA2MEM = /opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/data2mem
>
> #PROJROOT = ../../..
> #TOPLEVEL = ~/riscv/ise/bonfire/papilio_pro_dram_toplevel.bit
> PLATFORMDIR=../platform
>
> LINKDEF?=$(PLATFORMDIR)/$(PLATFORM)/firmware.ld
>
> TARGET_CFLAGS +=  -march=$(ARCH) -mabi=$(ABI) -Wall -Os -g
>  -fomit-frame-pointer \
> -ffreestanding -fno-builtin  \
> -Wall -Werror=implicit-function-declaration \
> -D$(PLATFORM)
>
> ifeq ($(SIMULATOR), 1)
>   TARGET_CFLAGS+=-DSIMULATOR
> endif
>
> TARGET_CFLAGS+=-I$(PLATFORMDIR) -I$(PLATFORMDIR)/$(PLATFORM) -I../riscv
>
> # If we wish to build with no newlib
> #TARGET_LDFLAGS += -march=$(ARCH) -mabi=$(ABI) -nostartfiles   \
> -Wl,-m,elf32lriscv,--wrap=malloc,--wrap=sbrk --specs=nosys.specs
> -Wl,-T$(LINKDEF) \
> -Wl,--gc-sections
>
> TARGET_LDFLAGS += -march=$(ARCH) -mabi=$(ABI) -nostartfiles   \
> -Wl,-m,elf32lriscv,--wrap=_srand,--wrap=_rand  --specs=nano.specs
> -Wl,-Map=output.map,-T$(LINKDEF) \
> -Wl,--gc-sections
>
> all: sim_hello.hex memtest.hex nanotest.hex
>
> %.o : %.S
> $(TARGET_CC) $(TARGET_CFLAGS)  -c $<
>
> %.o : %.c
> $(TARGET_CC) $(TARGET_CFLAGS) -c $<
>
> %.hex : %.elf
> $(TARGET_OBJCOPY) -S -O binary  $< $(basename $@).bin
> $(HEXDUMP) -v -e '1/4 "%08x\n"' $(basename $@).bin >$@
> $(TARGET_PREFIX)-objdump -S -d $< >$(basename $@).lst
> #$(TARGET_PREFIX)-objdump -D $< >$(basename $@).S
> $(TARGET_PREFIX)-objdump -s $< >$(basename $@).dmp
> $(TARGET_PREFIX)-size  $<
>
> #sbrk.o: libwrap/sbrk.c
> # $(TARGET_CC) $(TARGET_CFLAGS) -DSIM -c libwrap/sbrk.c
>
> rand.o: libwrap/rand.c
> $(TARGET_CC) $(TARGET_CFLAGS) -DSIM -c libwrap/rand.c
>
> uart.o: uart.c
> $(TARGET_CC) $(TARGET_CFLAGS) -DSIM -c uart.c
>
> #sim_hello.o : sim_hello.c
> # $(TARGET_CC) $(TARGET_CFLAGS) -DSIM  -c sim_hello.c
>
> sim_hello.elf: start.o uart.o sim_hello.o snprintf.o console.o rand.o
> $(TARGET_LD) -o $@ $(TARGET_LDFLAGS) start.o uart.o sim_hello.o snprintf.o
> console.o rand.o
>
> #memtest.elf: start.o uart.o memtest.o snprintf.o console.o mempattern.o
> sbrk.o
> # $(TARGET_LD) -o $@ $(TARGET_LDFLAGS) start.o uart.o memtest.o snprintf.o
> console.o mempattern.o sbrk.o
>
> #nanotest.elf: start.o uart.o nanotest.o snprintf.o console.o mempattern.o
> sbrk.o
> # $(TARGET_LD) -o $@ $(TARGET_LDFLAGS) start.o uart.o nanotest.o snprintf.o
> console.o mempattern.o sbrk.o
>
> #jump0.elf : jump0.S
> # $(TARGET_LD) -o $@ -march=RV32IM -nostartfiles   -Wl,-T$(BOOTLINK) $<
>
> clean:
> rm -f *.o
> ---------------------------------------------------------------------------------------------.h>
> #include <stdlib.h>
>
> //#include <newlib.h>
> //#include <nano_malloc.h>
> #include <errno.h>
> #include <sys/types.h>
> #include <string.h>
> #include "bonfire.h"
> #include "uart.h"
> #include "bonfire_gpio.h"
> #include "mem_rw.h"
> #include "console.h"
>
> extern void* sbrk(incr);
>
>
> // ========================================
> // Main function
> // ========================================
> int main() {
>
>   setBaudRate(PLATFORM_BAUDRATE);
>
>   caddr_t *ptr = sbrk(0);
>   // code never returns from sbrk
>   if (ptr == (void *)-1) {
>     printk("Failed to allocate memory\r\n");
>   }
>   else {
>     printk("Allocated memory at : %p\r\n", (void *) &ptr);
>   }
>   return 0;
> }
> ------------------------------------------------------------------------------------
> --- Makefile
> --------------------------------------------------------------------
>
> .PHONY: all clean
>
> ARCH ?= rv32ia
> ABI=ilp32
> PLATFORM ?= S100_T35
>
> #TARGET_PREFIX ?= riscv32-unknown-linux-gnu
> TARGET_PREFIX ?= riscv32-unknown-elf
> TARGET_CC := $(TARGET_PREFIX)-gcc
> TARGET_LD := $(TARGET_PREFIX)-gcc
> TARGET_SIZE := $(TARGET_PREFIX)-size
> TARGET_OBJCOPY := $(TARGET_PREFIX)-objcopy
> HEXDUMP ?= hexdump
> DATA2MEM = /opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/data2mem
>
> #PROJROOT = ../../..
> #TOPLEVEL = ~/riscv/ise/bonfire/papilio_pro_dram_toplevel.bit
> PLATFORMDIR=../platform
>
> LINKDEF?=$(PLATFORMDIR)/$(PLATFORM)/firmware.ld
>
> TARGET_CFLAGS +=  -march=$(ARCH) -mabi=$(ABI) -Wall -Os -g
>  -fomit-frame-pointer \
> -ffreestanding -fno-builtin  \
> -Wall -Werror=implicit-function-declaration \
> -D$(PLATFORM)
>
> ifeq ($(SIMULATOR), 1)
>   TARGET_CFLAGS+=-DSIMULATOR
> endif
>
> TARGET_CFLAGS+=-I$(PLATFORMDIR) -I$(PLATFORMDIR)/$(PLATFORM) -I../riscv
>
> # If we wish to build with no newlib
> #TARGET_LDFLAGS += -march=$(ARCH) -mabi=$(ABI) -nostartfiles   \
> -Wl,-m,elf32lriscv,--wrap=malloc,--wrap=sbrk --specs=nosys.specs
> -Wl,-T$(LINKDEF) \
> -Wl,--gc-sections
>
> TARGET_LDFLAGS += -march=$(ARCH) -mabi=$(ABI) -nostartfiles   \
> -Wl,-m,elf32lriscv,--wrap=_srand,--wrap=_rand  --specs=nano.specs
> -Wl,-Map=output.map,-T$(LINKDEF) \
> -Wl,--gc-sections
>
> all: sim_hello.hex memtest.hex nanotest.hex
>
> %.o : %.S
> $(TARGET_CC) $(TARGET_CFLAGS)  -c $<
>
> %.o : %.c
> $(TARGET_CC) $(TARGET_CFLAGS) -c $<
>
> %.hex : %.elf
> $(TARGET_OBJCOPY) -S -O binary  $< $(basename $@).bin
> $(HEXDUMP) -v -e '1/4 "%08x\n"' $(basename $@).bin >$@
> $(TARGET_PREFIX)-objdump -S -d $< >$(basename $@).lst
> #$(TARGET_PREFIX)-objdump -D $< >$(basename $@).S
> $(TARGET_PREFIX)-objdump -s $< >$(basename $@).dmp
> $(TARGET_PREFIX)-size  $<
>
> #sbrk.o: libwrap/sbrk.c
> # $(TARGET_CC) $(TARGET_CFLAGS) -DSIM -c libwrap/sbrk.c
>
> rand.o: libwrap/rand.c
> $(TARGET_CC) $(TARGET_CFLAGS) -DSIM -c libwrap/rand.c
>
> uart.o: uart.c
> $(TARGET_CC) $(TARGET_CFLAGS) -DSIM -c uart.c
>
> #sim_hello.o : sim_hello.c
> # $(TARGET_CC) $(TARGET_CFLAGS) -DSIM  -c sim_hello.c
>
> sim_hello.elf: start.o uart.o sim_hello.o snprintf.o console.o rand.o
> $(TARGET_LD) -o $@ $(TARGET_LDFLAGS) start.o uart.o sim_hello.o snprintf.o
> console.o rand.o
>
> #memtest.elf: start.o uart.o memtest.o snprintf.o console.o mempattern.o
> sbrk.o
> # $(TARGET_LD) -o $@ $(TARGET_LDFLAGS) start.o uart.o memtest.o snprintf.o
> console.o mempattern.o sbrk.o
>
> #nanotest.elf: start.o uart.o nanotest.o snprintf.o console.o mempattern.o
> sbrk.o
> # $(TARGET_LD) -o $@ $(TARGET_LDFLAGS) start.o uart.o nanotest.o snprintf.o
> console.o mempattern.o sbrk.o
>
> #jump0.elf : jump0.S
> # $(TARGET_LD) -o $@ -march=RV32IM -nostartfiles   -Wl,-T$(BOOTLINK) $<
>
> clean:
> rm -f *.o
> ---------------------------------------------------------------------------------------------
>
>
> --
> Dave Nadler, USA East Coast voice (978) 263-0097, [email protected], Skype
>  Dave.Nadler1
>
>

-- 
Damien Towning
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.